From aee806ca8208068e2c0a3b9cb184fa6ccaed8362 Mon Sep 17 00:00:00 2001 From: LEDfan Date: Sun, 2 Jul 2017 08:18:54 +0200 Subject: [PATCH 01/35] Change roster items when user is created, deleted or changed --- appinfo/app.php | 5 ++ appinfo/application.php | 19 ++++++ lib/db/iqrosterpush.php | 70 ++++++++++++++++++++ lib/db/iqrosterpushmapper.php | 12 ++++ lib/db/stanzamapper.php | 1 + lib/hooks.php | 120 ++++++++++++++++++++++++++++++++++ lib/stanzahandlers/iq.php | 9 ++- 7 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 lib/db/iqrosterpush.php create mode 100644 lib/db/iqrosterpushmapper.php create mode 100644 lib/hooks.php diff --git a/appinfo/app.php b/appinfo/app.php index be126a18..9baf7848 100755 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -7,6 +7,8 @@ * Released under the MIT license * @author Klaus Herberth */ +use OCA\OJSXC\AppInfo\Application; + if (!interface_exists('\OCP\Settings\ISettings')) { \OCP\App::registerAdmin ( 'ojsxc', 'settings/admin' ); } @@ -69,6 +71,9 @@ $config->setAppValue('ojsxc', 'apiSecret', $apiSecret); } +$app = new Application(); +$app->getContainer()->query('UserHooks')->register(); + if (!class_exists("\\Sabre\\Xml\\Version")) { require_once __DIR__ . "/../vendor/autoload.php"; } diff --git a/appinfo/application.php b/appinfo/application.php index 6f1a8648..cfa6cfaa 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -3,6 +3,7 @@ namespace OCA\OJSXC\AppInfo; use OCA\OJSXC\Controller\HttpBindController; +use OCA\OJSXC\Db\IQRosterPushMapper; use OCA\OJSXC\Db\MessageMapper; use OCA\OJSXC\Db\PresenceMapper; use OCA\OJSXC\Db\Stanza; @@ -16,6 +17,7 @@ use OCA\OJSXC\ILock; use OCA\OJSXC\DbLock; use OCA\OJSXC\MemLock; +use OCA\OJSXC\Hooks; use OCP\IContainer; use OCP\IRequest; @@ -71,6 +73,14 @@ public function __construct(array $urlParams=array()){ ); }); + $container->registerService('IQRosterPushMapper', function(IContainer $c) use ($container) { + return new IQRosterPushMapper( + $container->getServer()->getDatabaseConnection(), + $c->query('Host'), + $c->query('StanzaLogger') + ); + }); + $container->registerService('StanzaMapper', function(IContainer $c) use ($container) { return new StanzaMapper( $container->getServer()->getDatabaseConnection(), @@ -140,6 +150,15 @@ public function __construct(array $urlParams=array()){ }); + $container->registerService('UserHooks', function($c) { + return new Hooks( + $c->query('ServerContainer')->getUserManager(), + $c->query('ServerContainer')->getUserSession(), + $c->query('Host'), + $c->query('IQRosterPushMapper') + ); + }); + } /** diff --git a/lib/db/iqrosterpush.php b/lib/db/iqrosterpush.php new file mode 100644 index 00000000..21380828 --- /dev/null +++ b/lib/db/iqrosterpush.php @@ -0,0 +1,70 @@ +write([ + [ + 'name' => 'iq', + 'attributes' => [ + 'to' => $this->to, + 'type' => 'set', + 'id' => uniqid() + ], + 'value' => [[ + 'name' => 'query', + 'attributes' => [ + 'xmlns' => 'jabber:iq:roster', + ], + 'value' => [ + "name" => "item", + "attributes" => [ + "jid" => $this->jid, + "name" => $this->name, + "subscription" => $this->subscription + ], + "value" => '' + ] + ]] + ] + ]); + } + +} \ No newline at end of file diff --git a/lib/db/iqrosterpushmapper.php b/lib/db/iqrosterpushmapper.php new file mode 100644 index 00000000..6fe7491b --- /dev/null +++ b/lib/db/iqrosterpushmapper.php @@ -0,0 +1,12 @@ +fetch()){ $row['stanza'] = preg_replace('/to="([^"@]*)"/', "to=\"$1@" .$this->host ."/internal\"", $row['stanza']); $row['stanza'] = preg_replace('/from="([^"@]*)"/', "from=\"$1@" .$this->host ."/internal\"", $row['stanza']); + $row['stanza'] = preg_replace('/jid="([^"@]*)"/', "jid=\"$1@" .$this->host ."/internal\"", $row['stanza']); $results[] = $this->mapRowToEntity($row); } $stmt->closeCursor(); diff --git a/lib/hooks.php b/lib/hooks.php new file mode 100644 index 00000000..29aecb69 --- /dev/null +++ b/lib/hooks.php @@ -0,0 +1,120 @@ +userManager = $userManager; + $this->userSession = $userSession; + $this->host = $host; + $this->iqRosterPushMapper = $iqRosterPushMapper; + } + + public function register() { + $this->userManager->listen('\OC\User', 'postCreateUser', [$this, 'onCreateUser']); + $this->userManager->listen('\OC\User', 'postDelete', [$this, 'onDeleteUser']); + $this->userSession->listen('\OC\User', 'changeUser', [$this, 'onChangeUser']); + } + + /** + * @brief when a new user is created, the roster of the users must be updated, + * by sending a roster push. + * Note that this can still be useful when the roster and contacts menu are + * merged, for the internal state. + * @see https://tools.ietf.org/html/rfc6121#section-2.1.6 + * @param IUser $user + * @param string $password + */ + public function onCreateUser(IUser $user, $password) { + $iq = new IQRosterPush(); + $iq->setJid($user->getUID()); + $iq->setName($user->getDisplayName()); + $iq->setSubscription('both'); + $iq->setFrom(''); + + + foreach ($this->userManager->search('') as $recipient) { + if($recipient->getUID() !== $user->getUID()) { + $iq->setTo($recipient->getUID()); + $this->iqRosterPushMapper->insert($iq); + } + } + } + + /** + * @brief when a new user is created, the roster of the users must be updated, + * by sending a roster push. + * Note that this can still be useful when the roster and contacts menu are + * merged, for the internal state. E.g. JSXC removes a chat window, when it + * receives this stanza. + * @see https://tools.ietf.org/html/rfc6121#section-2.1.6 + * @param IUser $user + */ + public function onDeleteUser(IUser $user) { + $iq = new IQRosterPush(); + $iq->setJid($user->getUID()); + $iq->setName($user->getDisplayName()); + $iq->setSubscription('remove'); + $iq->setFrom(''); + + + foreach ($this->userManager->search('') as $recipient) { + if($recipient->getUID() !== $user->getUID()) { + $iq->setTo($recipient->getUID()); + $this->iqRosterPushMapper->insert($iq); + } + } + } + + /** + * @brief when a use is changed, adapt the roster of the users. + * Note that this can still be useful when the roster and contacts menu are + * merged, for the internal state. E.g. JSXC removes a chat window, when it + * receives this stanza. + * @see https://tools.ietf.org/html/rfc6121#section-2.1.6 + * @param IUser $user + * @param string $feature feature which was changed. Enabled and displayName are supported. + * @param string $value + */ + public function onChangeUser(IUser $user, $feature, $value) { + if ($feature === "enabled") { + if ($value === "true") { + // if user is enabled, add to roster + $this->onCreateUser($user, ''); + + } else if ($value === "false") { + // if user is enabled, remove from roster + $this->onDeleteUser($user); + } + } else if ($feature === "displayName") { + // if the user was changed, resend the whole roster item + $this->onCreateUser($user, ''); + } + } + +} \ No newline at end of file diff --git a/lib/stanzahandlers/iq.php b/lib/stanzahandlers/iq.php index 2dafeb77..45fc13a5 100644 --- a/lib/stanzahandlers/iq.php +++ b/lib/stanzahandlers/iq.php @@ -14,6 +14,11 @@ */ class IQ extends StanzaHandler { + /** + * @var IUserManager + */ + private $userManager; + /** * IQ constructor. * @@ -34,13 +39,13 @@ public function __construct($userId, $host, IUserManager $userManager) { public function handle(array $stanza) { $this->to = $this->getAttribute($stanza, 'to'); - if ($stanza['value'][0]['name'] === '{jabber:iq:roster}query'){ + if ($stanza['value'][0]['name'] === '{jabber:iq:roster}query') { $id = $stanza['attributes']['id']; $iqRoster = new IQRoster(); $iqRoster->setType('result'); $iqRoster->setTo($this->from); $iqRoster->setQid($id); - foreach($this->userManager->search('') as $user){ + foreach ($this->userManager->search('') as $user) { if($user->getUID() !== $this->userId) { $iqRoster->addItem($user->getUID() . '@' . $this->host, $user->getDisplayName()); } From 1202581d22334dce8c6b23d4518f00402095f664 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 7 Jul 2017 22:51:19 +0200 Subject: [PATCH 02/35] Fix loading of avatars --- ajax/getSettings.php | 1 + js/ojsxc.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ajax/getSettings.php b/ajax/getSettings.php index 4b52c228..83bee273 100644 --- a/ajax/getSettings.php +++ b/ajax/getSettings.php @@ -36,6 +36,7 @@ function validateBoolean($val) $data ['loginForm'] ['startMinimized'] = validateBoolean($config->getAppValue('ojsxc', 'xmppStartMinimized')); if ($data ['serverType'] === 'internal') { + $data['adminSettings']['xmppDomain'] = \OC::$server->query('Request')->getServerHost(); echo json_encode(array( 'result' => 'success', 'data' => $data, diff --git a/js/ojsxc.js b/js/ojsxc.js index 7601a9e9..4aa90a8e 100644 --- a/js/ojsxc.js +++ b/js/ojsxc.js @@ -218,6 +218,9 @@ jsxc.options.set('xmpp', { url: OC.generateUrl('apps/ojsxc/http-bind') }); + + jsxc.options.set('adminSettings', d.data.adminSettings); + if (d.data.loginForm) { jsxc.options.set('loginForm', { startMinimized: d.data.loginForm.startMinimized From 52ac44a18d9f3c5109e1aa8fb46f8dd3b76d7517 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 7 Jul 2017 23:01:32 +0200 Subject: [PATCH 03/35] Delete information stored about a user when deleting or disbaling this user --- appinfo/application.php | 4 +++- lib/db/presencemapper.php | 11 +++++++++++ lib/db/stanzamapper.php | 8 ++++++++ lib/hooks.php | 26 +++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/appinfo/application.php b/appinfo/application.php index cfa6cfaa..5a8b4778 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -155,7 +155,9 @@ public function __construct(array $urlParams=array()){ $c->query('ServerContainer')->getUserManager(), $c->query('ServerContainer')->getUserSession(), $c->query('Host'), - $c->query('IQRosterPushMapper') + $c->query('IQRosterPushMapper'), + $c->query('PresenceMapper'), + $c->query('StanzaMapper') ); }); diff --git a/lib/db/presencemapper.php b/lib/db/presencemapper.php index 72d065cd..23010444 100644 --- a/lib/db/presencemapper.php +++ b/lib/db/presencemapper.php @@ -200,4 +200,15 @@ public function updatePresence() { } } + /** + * @brief Deletes the presence records of a user. + * @param string $user + */ + public function deletePresence($user) { + $sql = "DELETE FROM `*PREFIX*ojsxc_presence` WHERE `userid` = ?"; + + $q = $this->db->prepare($sql); + $q->execute([$user]); + + } } \ No newline at end of file diff --git a/lib/db/stanzamapper.php b/lib/db/stanzamapper.php index b1fbd08c..820c9e99 100644 --- a/lib/db/stanzamapper.php +++ b/lib/db/stanzamapper.php @@ -81,4 +81,12 @@ public function findByTo($to){ return $results; } + /** + * @brief Deletes all stanzas addressed to a user. + * @param $uid + */ + public function deleteByTo($uid) { + $this->execute("DELETE FROM *PREFIX*ojsxc_stanzas WHERE `to`=?", [$uid]); + } + } \ No newline at end of file diff --git a/lib/hooks.php b/lib/hooks.php index 29aecb69..9c61b536 100644 --- a/lib/hooks.php +++ b/lib/hooks.php @@ -4,6 +4,8 @@ use OCA\OJSXC\Db\IQRosterPush; use OCA\OJSXC\Db\IQRosterPushMapper; +use OCA\OJSXC\Db\PresenceMapper; +use OCA\OJSXC\Db\StanzaMapper; use OCP\IUserManager; use OCP\IUser; @@ -28,11 +30,27 @@ class Hooks { */ private $userSession; - public function __construct(IUserManager $userManager, IUserSession $userSession, $host, IQRosterPushMapper $iqRosterPushMapper){ + /** + * @var PresenceMapper + */ + private $presenceMapper; + + /** + * @var StanzaMapper + */ + private $stanzaMapper; + + public function __construct(IUserManager $userManager, + IUserSession $userSession, $host, + IQRosterPushMapper $iqRosterPushMapper, + PresenceMapper $presenceMapper, + StanzaMapper $stanzaMapper) { $this->userManager = $userManager; $this->userSession = $userSession; $this->host = $host; $this->iqRosterPushMapper = $iqRosterPushMapper; + $this->presenceMapper = $presenceMapper; + $this->stanzaMapper = $stanzaMapper; } public function register() { @@ -89,6 +107,12 @@ public function onDeleteUser(IUser $user) { $this->iqRosterPushMapper->insert($iq); } } + + // delete the presence record of this user + $this->presenceMapper->deletePresence($user->getUID()); + + // delete all stanzas addressed to this user + $this->stanzaMapper->deleteByTo($user->getUID()); } /** From 7bf7072bf6ba377ae96ab294055b2f502ee7eefd Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Sat, 8 Jul 2017 07:51:27 +0200 Subject: [PATCH 04/35] Fix side effects of running updatePresence in the constructor of PresenceMapper --- lib/controller/httpbindcontroller.php | 1 + lib/db/presencemapper.php | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/controller/httpbindcontroller.php b/lib/controller/httpbindcontroller.php index 91655399..4d4b0dd6 100644 --- a/lib/controller/httpbindcontroller.php +++ b/lib/controller/httpbindcontroller.php @@ -157,6 +157,7 @@ public function __construct($appName, */ public function index() { $this->lock->setLock(); + $this->presenceMapper->updatePresence(); $input = $this->body; $longpoll = true; // set to false when the response should directly be returned and no polling should be done $longpollStart = true; // start the first long poll cycle diff --git a/lib/db/presencemapper.php b/lib/db/presencemapper.php index 23010444..f0b5f312 100644 --- a/lib/db/presencemapper.php +++ b/lib/db/presencemapper.php @@ -76,8 +76,6 @@ public function __construct(IDBConnection $db, $host, $userId, MessageMapper $me $this->messageMapper = $messageMapper; $this->newContentContainer = $newContentContainer; $this->timeout = $timeout; - - $this->updatePresence(); } /** From 00b268bad44a94e3c2f798e4209aa6220a59cc7a Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 10 Jul 2017 07:30:43 +0200 Subject: [PATCH 05/35] Add command to refresh roster of all users --- appinfo/application.php | 21 ++++++++- appinfo/register_command.php | 8 ++++ lib/Command/RefreshRoster.php | 48 +++++++++++++++++++++ lib/RosterPush.php | 80 +++++++++++++++++++++++++++++++++++ lib/hooks.php | 42 +++--------------- 5 files changed, 160 insertions(+), 39 deletions(-) create mode 100644 appinfo/register_command.php create mode 100644 lib/Command/RefreshRoster.php create mode 100644 lib/RosterPush.php diff --git a/appinfo/application.php b/appinfo/application.php index 5a8b4778..310373e5 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -2,6 +2,7 @@ namespace OCA\OJSXC\AppInfo; +use OCA\OJSXC\Command\RefreshRoster; use OCA\OJSXC\Controller\HttpBindController; use OCA\OJSXC\Db\IQRosterPushMapper; use OCA\OJSXC\Db\MessageMapper; @@ -9,6 +10,7 @@ use OCA\OJSXC\Db\Stanza; use OCA\OJSXC\Db\StanzaMapper; use OCA\OJSXC\NewContentContainer; +use OCA\OJSXC\RosterPush; use OCA\OJSXC\StanzaHandlers\IQ; use OCA\OJSXC\StanzaHandlers\Message; use OCA\OJSXC\StanzaHandlers\Presence; @@ -150,17 +152,32 @@ public function __construct(array $urlParams=array()){ }); + $container->registerService('RosterPush', function($c) { + return new RosterPush( + $c->query('ServerContainer')->getUserManager(), + $c->query('ServerContainer')->getUserSession(), + $c->query('Host'), + $c->query('IQRosterPushMapper') + ); + }); + $container->registerService('UserHooks', function($c) { return new Hooks( $c->query('ServerContainer')->getUserManager(), $c->query('ServerContainer')->getUserSession(), - $c->query('Host'), - $c->query('IQRosterPushMapper'), + $c->query('RosterPush'), $c->query('PresenceMapper'), $c->query('StanzaMapper') ); }); + $container->registerService('RefreshRosterCommand', function($c) { + return new RefreshRoster( + $c->query('ServerContainer')->getUserManager(), + $c->query('RosterPush') + ); + }); + } /** diff --git a/appinfo/register_command.php b/appinfo/register_command.php new file mode 100644 index 00000000..fb69e9fd --- /dev/null +++ b/appinfo/register_command.php @@ -0,0 +1,8 @@ +add($app->getContainer()->query('RefreshRosterCommand')); \ No newline at end of file diff --git a/lib/Command/RefreshRoster.php b/lib/Command/RefreshRoster.php new file mode 100644 index 00000000..46e20eee --- /dev/null +++ b/lib/Command/RefreshRoster.php @@ -0,0 +1,48 @@ +userManager = $userManager; + $this->rosterPush = $rosterPush; + + } + + protected function configure() { + $this->setName('ojsxc:refresh-roster'); + $this->setDescription('Refresh the roster of all users'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + + $users = $this->userManager->search(''); + + foreach ($users as $user) { + $this->rosterPush->createOrUpdateRosterItem($user); + } + + $output->writeln("Refreshed " . count($users) . " rosters. "); + } + +} diff --git a/lib/RosterPush.php b/lib/RosterPush.php new file mode 100644 index 00000000..ac317f8a --- /dev/null +++ b/lib/RosterPush.php @@ -0,0 +1,80 @@ +userManager = $userManager; + $this->userSession = $userSession; + $this->host = $host; + $this->iqRosterPushMapper = $iqRosterPushMapper; + } + + /** + * @see https://tools.ietf.org/html/rfc6121#section-2.1.6 + * @param IUser $user + */ + public function createOrUpdateRosterItem(IUser $user) { + $iq = new IQRosterPush(); + $iq->setJid($user->getUID()); + $iq->setName($user->getDisplayName()); + $iq->setSubscription('both'); + $iq->setFrom(''); + + + foreach ($this->userManager->search('') as $recipient) { + if($recipient->getUID() !== $user->getUID()) { + $iq->setTo($recipient->getUID()); + $this->iqRosterPushMapper->insert($iq); + } + } + } + + /** + * @see https://tools.ietf.org/html/rfc6121#section-2.1.6 + * @param IUser $user + */ + public function removeRosterItem(IUser $user) { + $iq = new IQRosterPush(); + $iq->setJid($user->getUID()); + $iq->setName($user->getDisplayName()); + $iq->setSubscription('remove'); + $iq->setFrom(''); + + + foreach ($this->userManager->search('') as $recipient) { + if($recipient->getUID() !== $user->getUID()) { + $iq->setTo($recipient->getUID()); + $this->iqRosterPushMapper->insert($iq); + } + } + } +} \ No newline at end of file diff --git a/lib/hooks.php b/lib/hooks.php index 9c61b536..e04ed7e9 100644 --- a/lib/hooks.php +++ b/lib/hooks.php @@ -18,13 +18,6 @@ class Hooks { */ private $userManager; - /** - * @var IQRosterPushMapper - */ - private $iqRosterPushMapper; - - private $host; - /** * @var IUserSession */ @@ -41,14 +34,13 @@ class Hooks { private $stanzaMapper; public function __construct(IUserManager $userManager, - IUserSession $userSession, $host, - IQRosterPushMapper $iqRosterPushMapper, + IUserSession $userSession, + RosterPush $rosterPush, PresenceMapper $presenceMapper, StanzaMapper $stanzaMapper) { $this->userManager = $userManager; $this->userSession = $userSession; - $this->host = $host; - $this->iqRosterPushMapper = $iqRosterPushMapper; + $this->rosterPush = $rosterPush; $this->presenceMapper = $presenceMapper; $this->stanzaMapper = $stanzaMapper; } @@ -69,19 +61,7 @@ public function register() { * @param string $password */ public function onCreateUser(IUser $user, $password) { - $iq = new IQRosterPush(); - $iq->setJid($user->getUID()); - $iq->setName($user->getDisplayName()); - $iq->setSubscription('both'); - $iq->setFrom(''); - - - foreach ($this->userManager->search('') as $recipient) { - if($recipient->getUID() !== $user->getUID()) { - $iq->setTo($recipient->getUID()); - $this->iqRosterPushMapper->insert($iq); - } - } + $this->rosterPush->createOrUpdateRosterItem($user); } /** @@ -94,19 +74,7 @@ public function onCreateUser(IUser $user, $password) { * @param IUser $user */ public function onDeleteUser(IUser $user) { - $iq = new IQRosterPush(); - $iq->setJid($user->getUID()); - $iq->setName($user->getDisplayName()); - $iq->setSubscription('remove'); - $iq->setFrom(''); - - - foreach ($this->userManager->search('') as $recipient) { - if($recipient->getUID() !== $user->getUID()) { - $iq->setTo($recipient->getUID()); - $this->iqRosterPushMapper->insert($iq); - } - } + $this->rosterPush->removeRosterItem($user); // delete the presence record of this user $this->presenceMapper->deletePresence($user->getUID()); From 1f35830432f448264cd4383fba4447e194835ae0 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 14 Jul 2017 07:30:04 +0200 Subject: [PATCH 06/35] Make filenames lowercase for stable9 --- .../RefreshRoster.php => command/refreshroster.php} | 8 ++++---- lib/{RosterPush.php => rosterpush.php} | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename lib/{Command/RefreshRoster.php => command/refreshroster.php} (88%) rename lib/{RosterPush.php => rosterpush.php} (98%) diff --git a/lib/Command/RefreshRoster.php b/lib/command/refreshroster.php similarity index 88% rename from lib/Command/RefreshRoster.php rename to lib/command/refreshroster.php index 46e20eee..0ee81880 100644 --- a/lib/Command/RefreshRoster.php +++ b/lib/command/refreshroster.php @@ -2,14 +2,14 @@ namespace OCA\OJSXC\Command; -use OCA\OJSXC\RosterPush; +use OCA\OJSXC\rosterpush; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class RefreshRoster extends Command { +class refreshroster extends Command { /** * @var IUserManager @@ -17,12 +17,12 @@ class RefreshRoster extends Command { private $userManager; /** - * @var RosterPush + * @var rosterpush */ private $rosterPush; public function __construct(IUserManager $userManager, - RosterPush $rosterPush) { + rosterpush $rosterPush) { parent::__construct(); $this->userManager = $userManager; $this->rosterPush = $rosterPush; diff --git a/lib/RosterPush.php b/lib/rosterpush.php similarity index 98% rename from lib/RosterPush.php rename to lib/rosterpush.php index ac317f8a..527474b8 100644 --- a/lib/RosterPush.php +++ b/lib/rosterpush.php @@ -10,7 +10,7 @@ use OCP\IUser; use OCP\IUserSession; -class RosterPush { +class rosterpush { /** * @var IUserManager From 0a0c6cef4f6bb8695691771b51ea0aa0aa299cb8 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Sat, 15 Jul 2017 09:07:05 +0200 Subject: [PATCH 07/35] When storing stanzas in the database always use the lowercase UserId fixes #35. There are two problems: - NC accepts to login with a username which has a different casing than the username in the DB. - the front-end sends "to" usernames as lowercase The solution is to convert the username of the current logged in user to lowercase and always use lowercase usernames in the DB. Signed-off-by: Tobia De Koninck --- appinfo/application.php | 22 +++++++++++++++------- lib/db/stanza.php | 20 ++++++++++++++++---- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/appinfo/application.php b/appinfo/application.php index 310373e5..4bd94f69 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -47,7 +47,7 @@ public function __construct(array $urlParams=array()){ return new HttpBindController( $c->query('AppName'), $c->query('Request'), - $c->query('UserId'), + $c->query('OJSXC_UserId'), $c->query('StanzaMapper'), $c->query('IQHandler'), $c->query('MessageHandler'), @@ -95,7 +95,7 @@ public function __construct(array $urlParams=array()){ return new PresenceMapper( $container->getServer()->getDatabaseConnection(), $c->query('Host'), - $c->query('UserId'), + $c->query('OJSXC_UserId'), $c->query('MessageMapper'), $c->query('NewContentContainer'), self::$config['polling']['timeout'] @@ -108,7 +108,7 @@ public function __construct(array $urlParams=array()){ */ $container->registerService('IQHandler', function(IContainer $c) { return new IQ( - $c->query('UserId'), + $c->query('OJSXC_UserId'), $c->query('Host'), $c->query('OCP\IUserManager') ); @@ -116,7 +116,7 @@ public function __construct(array $urlParams=array()){ $container->registerService('PresenceHandler', function(IContainer $c) { return new Presence( - $c->query('UserId'), + $c->query('OJSXC_UserId'), $c->query('Host'), $c->query('PresenceMapper'), $c->query('MessageMapper') @@ -125,7 +125,7 @@ public function __construct(array $urlParams=array()){ $container->registerService('MessageHandler', function(IContainer $c) { return new Message( - $c->query('UserId'), + $c->query('OJSXC_UserId'), $c->query('Host'), $c->query('MessageMapper') ); @@ -178,6 +178,14 @@ public function __construct(array $urlParams=array()){ ); }); + /** + * A modified userID for use in OJSXC. + * This is automatically made lowercase. + */ + $container->registerService('OJSXC_UserId', function(IContainer $c) { + return strtolower($c->query('UserId')); + }); + } /** @@ -193,7 +201,7 @@ private function getLock() { } else if ($cache->isAvailable()) { $memcache = $cache->create('ojsxc'); return new MemLock( - $c->query('UserId'), + $c->query('OJSXC_UserId'), $memcache ); } else { @@ -203,7 +211,7 @@ private function getLock() { // default return new DbLock( - $c->query('UserId'), + $c->query('OJSXC_UserId'), $c->query('OCP\IConfig'), $c->getServer()->getDatabaseConnection() ); diff --git a/lib/db/stanza.php b/lib/db/stanza.php index 1d897232..eae2929e 100644 --- a/lib/db/stanza.php +++ b/lib/db/stanza.php @@ -13,11 +13,7 @@ * @package OCA\OJSXC\Db * @brief this class is used as the entity which is fetched from the stanza table OR extended by a specific stanza * for inserting into the stanza table - * @method string getTo() - * @method string getFrom() * @method string getStanza() - * @method void setTo($to) - * @method void setFrom($from) * @method void setStanza($stanza) */ class Stanza extends Entity implements XmlSerializable{ @@ -41,6 +37,22 @@ public function __construct($stanza='') { */ public $stanza; + public function getTo() { + return $this->to; + } + + public function setTo($userId) { + $this->to = strtolower($userId); + } + + public function setFrom($userId) { + $this->from = strtolower($userId); + } + + public function getFrom() { + return $this->from; + } + public function xmlSerialize(Writer $writer) { $writer->writeRaw($this->getStanza()); } From 55d3cfcac6ff475233cef5a2bc2b60814607192b Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Sun, 16 Jul 2017 09:09:32 +0200 Subject: [PATCH 08/35] Fix wrong casing of class names --- lib/command/refreshroster.php | 8 ++++---- lib/rosterpush.php | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/command/refreshroster.php b/lib/command/refreshroster.php index 0ee81880..46e20eee 100644 --- a/lib/command/refreshroster.php +++ b/lib/command/refreshroster.php @@ -2,14 +2,14 @@ namespace OCA\OJSXC\Command; -use OCA\OJSXC\rosterpush; +use OCA\OJSXC\RosterPush; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class refreshroster extends Command { +class RefreshRoster extends Command { /** * @var IUserManager @@ -17,12 +17,12 @@ class refreshroster extends Command { private $userManager; /** - * @var rosterpush + * @var RosterPush */ private $rosterPush; public function __construct(IUserManager $userManager, - rosterpush $rosterPush) { + RosterPush $rosterPush) { parent::__construct(); $this->userManager = $userManager; $this->rosterPush = $rosterPush; diff --git a/lib/rosterpush.php b/lib/rosterpush.php index 527474b8..6634540a 100644 --- a/lib/rosterpush.php +++ b/lib/rosterpush.php @@ -4,13 +4,12 @@ use OCA\OJSXC\Db\IQRosterPush; use OCA\OJSXC\Db\IQRosterPushMapper; -use OCA\OJSXC\Db\PresenceMapper; use OCP\IUserManager; use OCP\IUser; use OCP\IUserSession; -class rosterpush { +class RosterPush { /** * @var IUserManager From 8b37e629f25f086029f8e38411eae6037307807e Mon Sep 17 00:00:00 2001 From: Marcel Waldvogel Date: Thu, 20 Jul 2017 17:06:39 +0200 Subject: [PATCH 09/35] Allow authentication with app passwords Necessary for SAML and other "passwordless" logins. Fixes jsxc/xmpp-cloud-auth#26 --- ajax/externalApi.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ajax/externalApi.php b/ajax/externalApi.php index 7f625bb4..5d70aba9 100644 --- a/ajax/externalApi.php +++ b/ajax/externalApi.php @@ -45,12 +45,15 @@ function checkPassword() { if(!empty($_POST['password']) && !empty($_POST['username'])) { if(!empty($_POST['domain'])) { - $currentUser = \OC::$server->getUserManager()->checkPassword($_POST['username'] . "@" . $_POST['domain'], $_POST['password']); + $loggedIn = \OC::$server->getUserSession()->login($_POST['username'] . "@" . $_POST['domain'], $_POST['password']); } - if($currentUser == null) { - $currentUser = \OC::$server->getUserManager()->checkPassword($_POST['username'], $_POST['password']); + if(!$loggedIn) { + $loggedIn = \OC::$server->getUserSession()->login($_POST['username'], $_POST['password']); } } + if ($loggedIn) { + $currentUser = \OC::$server->getUserSession()->getUser(); + } if (!$currentUser) { echo json_encode(array( From 7c1c9c6bfd9340f023c54012d96d5e56b5b667d3 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 13:31:42 +0200 Subject: [PATCH 10/35] transfer ajax/getSettings --- ajax/getSettings.php | 116 ----------- appinfo/application.php | 10 + appinfo/routes.php | 4 +- js/ojsxc.js | 2 +- .../HttpBindController.php} | 0 lib/Controller/SettingsController.php | 180 ++++++++++++++++++ 6 files changed, 192 insertions(+), 120 deletions(-) delete mode 100644 ajax/getSettings.php rename lib/{controller/httpbindcontroller.php => Controller/HttpBindController.php} (100%) create mode 100644 lib/Controller/SettingsController.php diff --git a/ajax/getSettings.php b/ajax/getSettings.php deleted file mode 100644 index 4b52c228..00000000 --- a/ajax/getSettings.php +++ /dev/null @@ -1,116 +0,0 @@ -getUserSession()->getUser(); -} else if(!empty($_POST['password']) && !empty($_POST['username'])) { - $currentUser = \OC::$server->getUserManager()->checkPassword($_POST['username'], $_POST['password']); -} - -if (!$currentUser) { - echo json_encode(array( - 'result' => 'noauth', - )); - exit(); -} - -$currentUID = $currentUser->getUID(); - -$config = \OC::$server->getConfig(); - -$serverType = $config->getAppValue('ojsxc', 'serverType'); - -$data = array(); -$data ['xmpp'] = array(); -$data ['serverType'] = (!empty($serverType))? $serverType : 'internal'; -$data ['loginForm'] ['startMinimized'] = validateBoolean($config->getAppValue('ojsxc', 'xmppStartMinimized')); - -if ($data ['serverType'] === 'internal') { - echo json_encode(array( - 'result' => 'success', - 'data' => $data, - )); - - exit; -} - -$data ['screenMediaExtension']['firefox'] = trim($config->getAppValue('ojsxc', 'firefoxExtension')); -$data ['screenMediaExtension']['chrome'] = trim($config->getAppValue('ojsxc', 'chromeExtension')); - -$data ['xmpp'] ['url'] = trim($config->getAppValue('ojsxc', 'boshUrl')); -$data ['xmpp'] ['domain'] = trim($config->getAppValue('ojsxc', 'xmppDomain')); -$data ['xmpp'] ['resource'] = trim($config->getAppValue('ojsxc', 'xmppResource')); -$data ['xmpp'] ['overwrite'] = validateBoolean($config->getAppValue('ojsxc', 'xmppOverwrite')); -$data ['xmpp'] ['onlogin'] = null; - -$data ['adminSettings'] = [ - 'xmppDomain' => trim($config->getAppValue('ojsxc', 'xmppDomain')) -]; - -if (validateBoolean($config->getAppValue('ojsxc', 'xmppPreferMail'))) { - $mail = $config->getUserValue($currentUID,'settings','email'); - - if ($mail !== null) { - list($u, $d) = explode("@", $mail, 2); - if ($d !== null && $d !== "") { - $data ['xmpp'] ['username'] = $u; - $data ['xmpp'] ['domain'] = $d; - } - } -} - -if (validateBoolean($config->getAppValue('ojsxc', 'timeLimitedToken'))) { - if(!is_string($data['xmpp']['username'])) { - $data['xmpp']['username'] = $currentUID; - } - - $jid = $data['xmpp']['username'] . '@' . $data['xmpp']['domain']; - $expiry = time() + 60*60; - $secret = $config->getAppValue('ojsxc', 'apiSecret'); - - $version = hex2bin('00'); - $secretID = substr(hash('sha256', $secret, true), 0, 2); - $header = $secretID.pack('N', $expiry); - $challenge = $version.$header.$jid; - $hmac = hash_hmac('sha256', $challenge, $secret, true); - $token = $version.substr($hmac, 0, 16).$header; - - // format as "user-friendly" base64 - $token = str_replace('=', '', strtr(base64_encode($token), - 'OIl', '-$%')); - - $data['xmpp']['password'] = $token; -} - -$options = $config->getUserValue($currentUID, 'ojsxc', 'options'); - -if ($options !== null) { - $options = (array) json_decode($options, true); - - if (is_array($options)) { - foreach ($options as $prop => $value) { - if ($prop !== 'xmpp' || $data ['xmpp'] ['overwrite']) { - foreach ($value as $key => $v) { - if ($v !== '') { - $data [$prop] [$key] = ($v === 'false' || $v === 'true') ? validateBoolean($v) : $v; - } - } - } - } - } -} - -echo json_encode(array( - 'result' => 'success', - 'data' => $data, -)); diff --git a/appinfo/application.php b/appinfo/application.php index 6f1a8648..0b08cc49 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -2,6 +2,7 @@ namespace OCA\OJSXC\AppInfo; +use OCA\OJSXC\Controller\SettingsController; use OCA\OJSXC\Controller\HttpBindController; use OCA\OJSXC\Db\MessageMapper; use OCA\OJSXC\Db\PresenceMapper; @@ -60,6 +61,15 @@ public function __construct(array $urlParams=array()){ ); }); + $container->registerService('SettingsController', function(IContainer $c) { + return new SettingsController( + $c->query('AppName'), + $c->query('Request'), + $c->query('Config'), + $c->query('UserManager') + ); + }); + /** * Database Layer */ diff --git a/appinfo/routes.php b/appinfo/routes.php index d201a7de..8f473e77 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -10,9 +10,6 @@ use \OCA\OJSXC\AppInfo\Application; -$this->create('ojsxc_ajax_getsettings', 'ajax/getSettings.php') - ->actionInclude('ojsxc/ajax/getSettings.php'); - $this->create('ojsxc_ajax_getturncredentials', 'ajax/getTurnCredentials.php') ->actionInclude('ojsxc/ajax/getTurnCredentials.php'); @@ -32,6 +29,7 @@ $application->registerRoutes($this, array( 'routes' => array( array('name' => 'http_bind#index', 'url' => '/http-bind', 'verb' => 'POST'), + array('name' => 'settings#index', 'url' => '/settings', 'verb' => 'POST'), ) )); ?> diff --git a/js/ojsxc.js b/js/ojsxc.js index 7601a9e9..c7d6669b 100644 --- a/js/ojsxc.js +++ b/js/ojsxc.js @@ -199,7 +199,7 @@ function loadSettings(username, password, cb) { $.ajax({ type: 'POST', - url: OC.filePath('ojsxc', 'ajax', 'getSettings.php'), + url: OC.generateUrl('apps/ojsxc/settings'), data: { username: username, password: password diff --git a/lib/controller/httpbindcontroller.php b/lib/Controller/HttpBindController.php similarity index 100% rename from lib/controller/httpbindcontroller.php rename to lib/Controller/HttpBindController.php diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php new file mode 100644 index 00000000..e6f98b50 --- /dev/null +++ b/lib/Controller/SettingsController.php @@ -0,0 +1,180 @@ +config = $config; + $this->userManager = $userManager; + + $this->userSession = \OC::$server->getUserSession(); + } + + /** + * @NoAdminRequired + * @PublicPage + */ + public function index() + { + $currentUser = $this->getCurrentUser(); + + if (!$currentUser) { + return array( + 'result' => 'noauth', + ); + } + + $currentUID = $currentUser->getUID(); + + $serverType = $this->getAppValue('serverType'); + + $data = array( + 'serverType' => (!empty($serverType))? $serverType : 'internal', + 'loginForm' => array( + 'startMinimized' => $this->getBooleanAppValue('xmppStartMinimized') + ) + ); + + if ($data ['serverType'] === 'internal') { + return array( + 'result' => 'success', + 'data' => $data, + ); + } + + $data ['screenMediaExtension'] = array( + 'firefox' => trim($this->getAppValue('firefoxExtension')), + 'chrome' => trim($this->getAppValue('chromeExtension')) + ); + + $data ['xmpp'] = array( + 'url' => trim($this->getAppValue('boshUrl')), + 'domain' => trim($this->getAppValue('xmppDomain')), + 'resource' => trim($this->getAppValue('xmppResource')), + 'overwrite' => $this->getBooleanAppValue('xmppOverwrite'), + 'onlogin' => null + ); + + $data ['adminSettings'] = array( + 'xmppDomain' => trim($this->getAppValue('xmppDomain')) + ); + + if ($this->getBooleanAppValue('xmppPreferMail')) { + $mail = $this->config->getUserValue($currentUID, 'settings', 'email'); + + if ($mail !== null) { + list($u, $d) = explode("@", $mail, 2); + if ($d !== null && $d !== "") { + $data ['xmpp'] ['username'] = $u; + $data ['xmpp'] ['domain'] = $d; + } + } + } + + if ($this->getBooleanAppValue('timeLimitedToken')) { + if (!is_string($data['xmpp']['username'])) { + $data['xmpp']['username'] = $currentUID; + } + + $this->generateTimeLimitedToken($data['xmpp']['username'], $data['xmpp']['domain']); + + $data['xmpp']['password'] = $token; + } + + $data = $this->overwriteByUserDefined($currentUID, $data); + + return array( + 'result' => 'success', + 'data' => $data, + ); + } + + private function getCurrentUser() + { + $currentUser = false; + + if (\OCP\User::isLoggedIn()) { + $currentUser = $this->userSession->getUser(); + } elseif (!empty($_POST['password']) && !empty($_POST['username'])) { + $currentUser = $this->userManager->checkPassword($_POST['username'], $_POST['password']); + } + + return $currentUser; + } + + private function generateTimeLimitedToken($node, $domain) + { + $jid = $node. '@' . $domain; + $expiry = time() + 60*60; + $secret = $this->getAppValue('apiSecret'); + + $version = hex2bin('00'); + $secretID = substr(hash('sha256', $secret, true), 0, 2); + $header = $secretID.pack('N', $expiry); + $challenge = $version.$header.$jid; + $hmac = hash_hmac('sha256', $challenge, $secret, true); + $token = $version.substr($hmac, 0, 16).$header; + + // format as "user-friendly" base64 + $token = str_replace('=', '', strtr(base64_encode($token), + 'OIl', '-$%')); + + return $token; + } + + private function overwriteByUserDefined($currentUID, $data) + { + $options = $this->config->getUserValue($currentUID, 'ojsxc', 'options'); + + if ($options !== null) { + $options = (array) json_decode($options, true); + + if (is_array($options)) { + foreach ($options as $prop => $value) { + if ($prop !== 'xmpp' || $data ['xmpp'] ['overwrite']) { + foreach ($value as $key => $v) { + if ($v !== '') { + $data [$prop] [$key] = ($v === 'false' || $v === 'true') ? validateBoolean($v) : $v; + } + } + } + } + } + } + + return $data; + } + + private function getBooleanAppValue($key) + { + return $this->validateBoolean($this->getAppValue($key)); + } + + private function getAppValue($key) + { + return $this->config->getAppValue($this->appName, $key); + } + + private function validateBoolean($val) + { + return $val === true || $val === 'true'; + } +} From ffa290c5f2a1263c978e347e13ce171f55dc5281 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 13:54:30 +0200 Subject: [PATCH 11/35] add fix from 1202581d22334dce8c6b23d4518f00402095f664 --- lib/Controller/SettingsController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index e6f98b50..ff940caa 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -54,6 +54,8 @@ public function index() ); if ($data ['serverType'] === 'internal') { + $data['adminSettings']['xmppDomain'] = $this->request->getServerHost(); + return array( 'result' => 'success', 'data' => $data, From ec05b2670e91b43c1d76ebb783bdb7e1fe022345 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 14:14:55 +0200 Subject: [PATCH 12/35] transfer ajax/setAdminSettings --- ajax/setAdminSettings.php | 51 ---------------------- appinfo/routes.php | 1 + js/settings/admin.js | 4 +- lib/Controller/SettingsController.php | 63 +++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 53 deletions(-) delete mode 100644 ajax/setAdminSettings.php diff --git a/ajax/setAdminSettings.php b/ajax/setAdminSettings.php deleted file mode 100644 index c97e04c7..00000000 --- a/ajax/setAdminSettings.php +++ /dev/null @@ -1,51 +0,0 @@ -getSession()->get('last-password-confirm'); -if ($majorVersion >= 11 && $lastConfirm < (time() - 30 * 60 + 15)) { - $l = \OC::$server->getL10N('core'); - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); - exit(); -} - -$config = \OC::$server->getConfig(); - -$config->setAppValue('ojsxc', 'serverType', $_POST ['serverType']); -$config->setAppValue('ojsxc', 'boshUrl', trim($_POST ['boshUrl'])); -$config->setAppValue('ojsxc', 'xmppDomain', trim($_POST ['xmppDomain'])); -$config->setAppValue('ojsxc', 'xmppResource', trim($_POST ['xmppResource'])); -$config->setAppValue('ojsxc', 'xmppOverwrite', getCheckboxValue($_POST ['xmppOverwrite'])); -$config->setAppValue('ojsxc', 'xmppStartMinimized', getCheckboxValue($_POST ['xmppStartMinimized'])); -$config->setAppValue('ojsxc', 'xmppPreferMail', getCheckboxValue($_POST ['xmppPreferMail'])); - -$config->setAppValue('ojsxc', 'iceUrl', trim($_POST ['iceUrl'])); -$config->setAppValue('ojsxc', 'iceUsername', trim($_POST ['iceUsername'])); -$config->setAppValue('ojsxc', 'iceCredential', $_POST ['iceCredential']); -$config->setAppValue('ojsxc', 'iceSecret', $_POST ['iceSecret']); -$config->setAppValue('ojsxc', 'iceTtl', $_POST ['iceTtl']); - -$config->setAppValue('ojsxc', 'timeLimitedToken', getCheckboxValue($_POST ['timeLimitedToken'])); - -$config->setAppValue('ojsxc', 'firefoxExtension', $_POST ['firefoxExtension']); -$config->setAppValue('ojsxc', 'chromeExtension', $_POST ['chromeExtension']); - -$externalServices = array(); -foreach($_POST['externalServices'] as $es) { - if (preg_match('/^(https:\/\/)?([\w\d*][\w\d-]*)(\.[\w\d-]+)+(:[\d]+)?$/', $es)) { - $externalServices[] = $es; - } -} -$config->setAppValue('ojsxc', 'externalServices', implode('|', $externalServices)); - -echo 'true'; - -function getCheckboxValue($var) { - return (isset($var)) ? $var : 'false'; -} diff --git a/appinfo/routes.php b/appinfo/routes.php index 8f473e77..793a5dea 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -30,6 +30,7 @@ 'routes' => array( array('name' => 'http_bind#index', 'url' => '/http-bind', 'verb' => 'POST'), array('name' => 'settings#index', 'url' => '/settings', 'verb' => 'POST'), + array('name' => 'settings#setAdmin', 'url' => '/settings/admin', 'verb' => 'POST'), ) )); ?> diff --git a/js/settings/admin.js b/js/settings/admin.js index 14a8e40b..4187ff7c 100644 --- a/js/settings/admin.js +++ b/js/settings/admin.js @@ -144,8 +144,8 @@ $(document).ready(function() { var status = $('#ojsxc .msg div'); status.html('wait Saving...'); - $.post(OC.filePath('ojsxc', 'ajax', 'setAdminSettings.php'), post, function(data) { - if (data) { + $.post(OC.generateUrl('apps/ojsxc/settings/admin'), post, function(data) { + if (data && data.status === 'success') { status.addClass('jsxc_success').text('Settings saved. Please log out and in again.'); } else { status.addClass('jsxc_fail').text('Error!'); diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index ff940caa..81eb781b 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -109,6 +109,50 @@ public function index() ); } + public function setAdmin() { + if ($this->isPasswordConfirmationRequired()) { + $l = \OC::$server->getL10N('core'); + + return array( + 'status' => 'error', + 'data' => array( + 'message' => $l->t('Password confirmation is required') + ) + ); + } + + $this->setAppValue('serverType', $_POST ['serverType']); + $this->setAppValue('boshUrl', trim($_POST ['boshUrl'])); + $this->setAppValue('xmppDomain', trim($_POST ['xmppDomain'])); + $this->setAppValue('xmppResource', trim($_POST ['xmppResource'])); + $this->setAppValue('xmppOverwrite', $this->getCheckboxValue($_POST ['xmppOverwrite'])); + $this->setAppValue('xmppStartMinimized', $this->getCheckboxValue($_POST ['xmppStartMinimized'])); + $this->setAppValue('xmppPreferMail', $this->getCheckboxValue($_POST ['xmppPreferMail'])); + + $this->setAppValue('iceUrl', trim($_POST ['iceUrl'])); + $this->setAppValue('iceUsername', trim($_POST ['iceUsername'])); + $this->setAppValue('iceCredential', $_POST ['iceCredential']); + $this->setAppValue('iceSecret', $_POST ['iceSecret']); + $this->setAppValue('iceTtl', $_POST ['iceTtl']); + + $this->setAppValue('timeLimitedToken', $this->getCheckboxValue($_POST ['timeLimitedToken'])); + + $this->setAppValue('firefoxExtension', $_POST ['firefoxExtension']); + $this->setAppValue('chromeExtension', $_POST ['chromeExtension']); + + $externalServices = array(); + foreach($_POST['externalServices'] as $es) { + if (preg_match('/^(https:\/\/)?([\w\d*][\w\d-]*)(\.[\w\d-]+)+(:[\d]+)?$/', $es)) { + $externalServices[] = $es; + } + } + $this->setAppValue('externalServices', implode('|', $externalServices)); + + return array( + 'status' => 'success' + ); + } + private function getCurrentUser() { $currentUser = false; @@ -175,8 +219,27 @@ private function getAppValue($key) return $this->config->getAppValue($this->appName, $key); } + private function setAppValue($key, $value) { + return $this->config->setAppValue($this->appName, $key, $value); + } + private function validateBoolean($val) { return $val === true || $val === 'true'; } + + private function isPasswordConfirmationRequired() { + $version = \OCP\Util::getVersion(); + preg_match('/^([0-9]+)\.', $version, $versionMatches); + $majorVersion = intval($versionMatches[1]); + + // copied from owncloud/settings/ajax/installapp.php + $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); + + return $majorVersion >= 11 && $lastConfirm < (time() - 30 * 60 + 15); + } + + private function getCheckboxValue($var) { + return (isset($var)) ? $var : 'false'; + } } From 1abf914368e2912b471234708cb2d0c428e3088c Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 14:18:07 +0200 Subject: [PATCH 13/35] drop nc 9 support (fix #40) --- .travis.yml | 2 -- appinfo/info.xml | 2 +- tests/travis/install-nc-stable9.sh | 17 ----------------- 3 files changed, 1 insertion(+), 20 deletions(-) delete mode 100755 tests/travis/install-nc-stable9.sh diff --git a/.travis.yml b/.travis.yml index 78c6bac3..5b238988 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,6 @@ matrix: env: DB=sqlite - php: 5.6 env: DB=pgsql - - php: 5.6 - env: DB=mysql BRANCH=stable9 - php: 5.6 env: DB=mysql BRANCH=stable10 - php: 5.6 diff --git a/appinfo/info.xml b/appinfo/info.xml index 27ca7663..653b180b 100755 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -38,7 +38,7 @@ xmlwriter dom - + 162257 diff --git a/tests/travis/install-nc-stable9.sh b/tests/travis/install-nc-stable9.sh deleted file mode 100755 index 48969354..00000000 --- a/tests/travis/install-nc-stable9.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -x -# set up postgresql -createuser -U travis -s oc_autotest -# set up mysql -mysql -e 'create database oc_autotest;' -mysql -u root -e "CREATE USER 'oc_autotest'@'localhost' IDENTIFIED BY '';" -mysql -u root -e "grant all on oc_autotest.* to 'oc_autotest'@'localhost';" -# install nextcloud -cd .. -#ncdev setup core --dir owncloud --branch $BRANCH --no-history -git clone https://github.com/nextcloud/server.git --recursive --depth 1 -b $BRANCH nextcloud -mv jsxc.nextcloud nextcloud/apps/ojsxc -phpenv config-add nextcloud/apps/ojsxc/tests/travis/php.ini -cd nextcloud -./occ maintenance:install --database-name oc_autotest --database-user oc_autotest --database-pass --admin-user admin --admin-pass admin --database $DB -./occ app:enable ojsxc -cd apps/ojsxc From 1292a9f6e8cce49dd784514df9cbd45035473f99 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 14:29:08 +0200 Subject: [PATCH 14/35] transfer ajax/setUserSettings --- ajax/setUserSettings.php | 17 ----------------- appinfo/routes.php | 1 + js/settings/personal.js | 4 ++-- lib/Controller/SettingsController.php | 20 ++++++++++++++++++++ 4 files changed, 23 insertions(+), 19 deletions(-) delete mode 100755 ajax/setUserSettings.php diff --git a/ajax/setUserSettings.php b/ajax/setUserSettings.php deleted file mode 100755 index b81ba8fb..00000000 --- a/ajax/setUserSettings.php +++ /dev/null @@ -1,17 +0,0 @@ -getConfig(); -$uid = \OC::$server->getUserSession()->getUser()->getUID(); - -$options = $config->getUserValue($uid, 'ojsxc', 'options'); -$options = json_decode($options, true); - -foreach($_POST as $key => $val) { - $options[$key] = $val; -} - -$config->setUserValue($uid, 'ojsxc', 'options', json_encode($options)); - -echo 'true'; diff --git a/appinfo/routes.php b/appinfo/routes.php index 793a5dea..0792ff73 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -31,6 +31,7 @@ array('name' => 'http_bind#index', 'url' => '/http-bind', 'verb' => 'POST'), array('name' => 'settings#index', 'url' => '/settings', 'verb' => 'POST'), array('name' => 'settings#setAdmin', 'url' => '/settings/admin', 'verb' => 'POST'), + array('name' => 'settings#setUser', 'url' => '/settings/user', 'verb' => 'POST'), ) )); ?> diff --git a/js/settings/personal.js b/js/settings/personal.js index 3838d409..e28ebc8b 100644 --- a/js/settings/personal.js +++ b/js/settings/personal.js @@ -16,12 +16,12 @@ $.ajax({ method: 'POST', - url: OC.filePath('ojsxc', 'ajax', 'setUserSettings.php'), + url: OC.generateUrl('apps/ojsxc/settings/user'), data: { loginForm: loginFormData }, success: function(data){ - if (data === 'true') { + if (data && data.status === 'success') { console.log('loginFormEnable saved.'); } } diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 81eb781b..7d4b15f6 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -153,6 +153,26 @@ public function setAdmin() { ); } + /** + * @NoAdminRequired + */ + public function setUser() { + $uid = $this->userSession->getUser()->getUID(); + + $options = $this->config->getUserValue($uid, 'ojsxc', 'options'); + $options = json_decode($options, true); + + foreach($_POST as $key => $val) { + $options[$key] = $val; + } + + $this->config->setUserValue($uid, 'ojsxc', 'options', json_encode($options)); + + return array( + 'status' => 'success' + ); + } + private function getCurrentUser() { $currentUser = false; From 92e06653061c8f3d31b1f664270505a8dec13878 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 14:43:14 +0200 Subject: [PATCH 15/35] transfer ajax/getTurnCredentials --- ajax/getTurnCredentials.php | 39 ------------------------ appinfo/routes.php | 1 + js/ojsxc.js | 2 +- lib/Controller/SettingsController.php | 43 +++++++++++++++++++++++++-- 4 files changed, 42 insertions(+), 43 deletions(-) delete mode 100644 ajax/getTurnCredentials.php diff --git a/ajax/getTurnCredentials.php b/ajax/getTurnCredentials.php deleted file mode 100644 index d7ac3d14..00000000 --- a/ajax/getTurnCredentials.php +++ /dev/null @@ -1,39 +0,0 @@ -getConfig(); -$secret = $config->getAppValue('ojsxc', 'iceSecret'); -$user = \OC::$server->getUserSession()->getUser()->getUID(); - -$ttl = $config->getAppValue('ojsxc', 'iceTtl', 3600 * 24); // one day (according to TURN-REST-API) -$url = $config->getAppValue('ojsxc', 'iceUrl'); -$url = preg_match('/^(turn|stun):/', $url) || empty($url) ? $url : "turn:$url"; - -$usernameTRA = $secret ? (time() + $ttl).':'.$user : $user; -$username = $config->getAppValue('ojsxc', 'iceUsername', ''); -$username = (!empty($username)) ? $username : $usernameTRA; - -$credentialTRA = ($secret) ? base64_encode(hash_hmac('sha1', $username, $secret, true)) : ''; -$credential = $config->getAppValue('ojsxc', 'iceCredential', ''); -$credential = (!empty($credential)) ? $credential : $credentialTRA; - -if (!empty($url)) { - $data = array( - 'ttl' => $ttl, - 'iceServers' => array( - array( - 'urls' => array($url), - 'credential' => $credential, - 'username' => $username, - ), - ), - ); -} else { - $data = array(); -} - -echo json_encode($data); diff --git a/appinfo/routes.php b/appinfo/routes.php index 0792ff73..b1a4dbfb 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -32,6 +32,7 @@ array('name' => 'settings#index', 'url' => '/settings', 'verb' => 'POST'), array('name' => 'settings#setAdmin', 'url' => '/settings/admin', 'verb' => 'POST'), array('name' => 'settings#setUser', 'url' => '/settings/user', 'verb' => 'POST'), + array('name' => 'settings#getIceServers', 'url' => '/settings/iceServers', 'verb' => 'GET'), ) )); ?> diff --git a/js/ojsxc.js b/js/ojsxc.js index f29979db..9d1f945d 100644 --- a/js/ojsxc.js +++ b/js/ojsxc.js @@ -332,7 +332,7 @@ rosterAppend: 'body', root: oc_appswebroots.ojsxc + '/js/jsxc', RTCPeerConfig: { - url: OC.filePath('ojsxc', 'ajax', 'getTurnCredentials.php') + url: OC.generateUrl('apps/ojsxc/settings/iceServers') }, displayRosterMinimized: function() { return OC.currentUser != null; diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 7d4b15f6..67a83902 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -173,6 +173,43 @@ public function setUser() { ); } + /** + * @NoAdminRequired + */ + public function getIceServers() { + $secret = $this->getAppValue('iceSecret'); + $uid = $this->userSession->getUser()->getUID(); + + $ttl = $this->getAppValue('iceTtl', 3600 * 24); // one day (according to TURN-REST-API) + $url = $this->getAppValue('iceUrl'); + $url = preg_match('/^(turn|stun):/', $url) || empty($url) ? $url : "turn:$url"; + + $usernameTRA = $secret ? (time() + $ttl).':'.$uid : $uid; + $username = $this->getAppValue('iceUsername', ''); + $username = (!empty($username)) ? $username : $usernameTRA; + + $credentialTRA = ($secret) ? base64_encode(hash_hmac('sha1', $username, $secret, true)) : ''; + $credential = $this->getAppValue('iceCredential', ''); + $credential = (!empty($credential)) ? $credential : $credentialTRA; + + if (!empty($url)) { + $data = array( + 'ttl' => $ttl, + 'iceServers' => array( + array( + 'urls' => array($url), + 'credential' => $credential, + 'username' => $username, + ), + ), + ); + } else { + $data = array(); + } + + return $data; + } + private function getCurrentUser() { $currentUser = false; @@ -218,7 +255,7 @@ private function overwriteByUserDefined($currentUID, $data) if ($prop !== 'xmpp' || $data ['xmpp'] ['overwrite']) { foreach ($value as $key => $v) { if ($v !== '') { - $data [$prop] [$key] = ($v === 'false' || $v === 'true') ? validateBoolean($v) : $v; + $data [$prop] [$key] = ($v === 'false' || $v === 'true') ? $this->validateBoolean($v) : $v; } } } @@ -234,9 +271,9 @@ private function getBooleanAppValue($key) return $this->validateBoolean($this->getAppValue($key)); } - private function getAppValue($key) + private function getAppValue($key, $default) { - return $this->config->getAppValue($this->appName, $key); + return $this->config->getAppValue($this->appName, $key, $default); } private function setAppValue($key, $value) { From 7472a627c012ebfc08d94b8bb7a545616ace97f0 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 15:02:07 +0200 Subject: [PATCH 16/35] return default value for empty app value --- lib/Controller/SettingsController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 67a83902..36ef9f42 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -271,9 +271,11 @@ private function getBooleanAppValue($key) return $this->validateBoolean($this->getAppValue($key)); } - private function getAppValue($key, $default) + private function getAppValue($key, $default = null) { - return $this->config->getAppValue($this->appName, $key, $default); + $value = $this->config->getAppValue($this->appName, $key, $default); + + return (empty($value)) ? $default : $value; } private function setAppValue($key, $value) { From d534e981f0bc229abcb6f5ab87db51db2d058bac Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 15:10:24 +0200 Subject: [PATCH 17/35] transfer ajax/getUsers --- ajax/getUsers.php | 34 --------------------------- appinfo/routes.php | 1 + js/ojsxc.js | 2 +- lib/Controller/SettingsController.php | 30 +++++++++++++++++++++++ 4 files changed, 32 insertions(+), 35 deletions(-) delete mode 100644 ajax/getUsers.php diff --git a/ajax/getUsers.php b/ajax/getUsers.php deleted file mode 100644 index 5b5790bd..00000000 --- a/ajax/getUsers.php +++ /dev/null @@ -1,34 +0,0 @@ -getConfig(); -$preferMail = $config->getAppValue('ojsxc', 'xmppPreferMail'); -$preferMail = $preferMail === true || $preferMail === 'true'; - -$userManager = \OC::$server->getUserManager(); -$users = $userManager->searchDisplayName((string) $_GET['search'], $limit, $offset); -$response = array(); - -foreach($users as $user) { - $uid = $user->getUID(); - $index = $uid; - - if ($preferMail) { - $mail = OCP\Config::getUserValue($uid, 'settings', 'email'); - - if (!empty($mail)) { - $index = $mail; - } - } - - $response[$index] = $user->getDisplayName(); -} - -echo json_encode($response); diff --git a/appinfo/routes.php b/appinfo/routes.php index b1a4dbfb..7bf684c7 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -33,6 +33,7 @@ array('name' => 'settings#setAdmin', 'url' => '/settings/admin', 'verb' => 'POST'), array('name' => 'settings#setUser', 'url' => '/settings/user', 'verb' => 'POST'), array('name' => 'settings#getIceServers', 'url' => '/settings/iceServers', 'verb' => 'GET'), + array('name' => 'settings#getUsers', 'url' => '/settings/users', 'verb' => 'GET'), ) )); ?> diff --git a/js/ojsxc.js b/js/ojsxc.js index 9d1f945d..0253f8c5 100644 --- a/js/ojsxc.js +++ b/js/ojsxc.js @@ -257,7 +257,7 @@ function getUsers(search, cb) { $.ajax({ type: 'GET', - url: OC.filePath('ojsxc', 'ajax', 'getUsers.php'), + url: OC.generateUrl('apps/ojsxc/settings/users'), data: { search: search }, diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 36ef9f42..910e2b7e 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -210,6 +210,36 @@ public function getIceServers() { return $data; } + /** + * @NoAdminRequired + */ + public function getUsers($search = '') { + $limit = 10; + $offset = 0; + + $preferMail = $this->getBooleanAppValue('xmppPreferMail'); + + $users = $this->userManager->searchDisplayName($search, $limit, $offset); + $response = array(); + + foreach($users as $user) { + $uid = $user->getUID(); + $index = $uid; + + if ($preferMail) { + $mail = $this->config->getUserValue($uid, 'settings', 'email'); + + if (!empty($mail)) { + $index = $mail; + } + } + + $response[$index] = $user->getDisplayName(); + } + + return $response; + } + private function getCurrentUser() { $currentUser = false; From 0fec051adbd374548bee4705fdb7c3a8f862d546 Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 17:01:42 +0200 Subject: [PATCH 18/35] transfer ajax/externalApi --- ajax/externalApi.php | 152 -------------------- appinfo/application.php | 25 ++++ appinfo/routes.php | 4 + lib/Controller/ExternalApiController.php | 173 +++++++++++++++++++++++ lib/Exceptions/SecurityException.php | 5 + lib/Middleware/ExternalApiMiddleware.php | 69 +++++++++ 6 files changed, 276 insertions(+), 152 deletions(-) delete mode 100644 ajax/externalApi.php create mode 100644 lib/Controller/ExternalApiController.php create mode 100644 lib/Exceptions/SecurityException.php create mode 100644 lib/Middleware/ExternalApiMiddleware.php diff --git a/ajax/externalApi.php b/ajax/externalApi.php deleted file mode 100644 index 7f625bb4..00000000 --- a/ajax/externalApi.php +++ /dev/null @@ -1,152 +0,0 @@ -getConfig(); -$apiSecret = $config->getAppValue('ojsxc', 'apiSecret'); - -function abort($msg) { - http_response_code(500); - - \OCP\Util::writeLog('ojsxc', 'ExAPI: Abort with message: '.$msg, \OCP\Util::WARN ); - - die(json_encode(array( - 'result' => 'error', - 'data' => array( - 'msg' => $msg - ), - ))); -} - -function stringOrEmpty($s) { - if(empty($s)) { - return ""; - } else { - return $s; - } -} - -function getUsername() { - if(!empty($_POST['username'])) { - if(!empty($_POST['domain'])) { - return $_POST['username'] . "@" . $_POST['domain']; - } else { - return $_POST['username']; - } - } else { - abort('No username provided'); - } -} - -function checkPassword() { - $currentUser = null; - - \OCP\Util::writeLog('ojsxc', 'ExAPI: Check password for user: '.stringOrEmpty($_POST['username'])."@".stringOrEmpty($_POST['domain']), \OCP\Util::INFO ); - - if(!empty($_POST['password']) && !empty($_POST['username'])) { - if(!empty($_POST['domain'])) { - $currentUser = \OC::$server->getUserManager()->checkPassword($_POST['username'] . "@" . $_POST['domain'], $_POST['password']); - } - if($currentUser == null) { - $currentUser = \OC::$server->getUserManager()->checkPassword($_POST['username'], $_POST['password']); - } - } - - if (!$currentUser) { - echo json_encode(array( - 'result' => 'noauth', - )); - exit(); - } - - $data = array(); - $data ['uid'] = $currentUser->getUID(); - - echo json_encode(array( - 'result' => 'success', - 'data' => $data, - )); -} - -function isUser() { - \OCP\Util::writeLog('ojsxc', 'ExAPI: Check if "'.stringOrEmpty($_POST['username'])."@".stringOrEmpty($_POST['domain']).'" exists', \OCP\Util::INFO ); - - $isUser = false; - - if(!empty($_POST['username'])) { - if(!empty($_POST['domain'])) { - $isUser = \OC::$server->getUserManager()->userExists($_POST['username'] . "@" . $_POST['domain']); - } - if(!$isUser) { - $isUser = \OC::$server->getUserManager()->userExists($_POST['username']); - } - } - - echo json_encode(array( - 'result' => 'success', - 'data' => array( - 'isUser' => $isUser - ) - )); -} - -function sharedRoster() { - $username = getUsername(); - $roster = []; - - $userGroups = \OC::$server->getGroupManager()->getUserIdGroups($username); - - foreach($userGroups as $userGroup) { - foreach($userGroup->getUsers() as $user) { - $uid = $user->getUID(); - - if(!$roster[$uid]) { - $roster[$uid] = [ - 'name' => $user->getDisplayName(), - 'groups' => [] - ]; - } - - $roster[$uid]['groups'][] = $userGroup->getDisplayName(); - } - } - - echo json_encode(array( - 'result' => 'success', - 'data' => array( - 'sharedRoster' => $roster - ) - )); -} - -// check if we have a signature -if ( ! isset( $_SERVER[ 'HTTP_X_JSXC_SIGNATURE' ] ) ) - abort( 'HTTP header "X-JSXC-Signature" is missing.' ); -else if ( ! extension_loaded( 'hash' ) ) - abort( 'Missing "hash" extension to check the secret code validity.' ); -else if ( ! $apiSecret) - abort( 'Missing secret.' ); - -// check if the algo is supported -list( $algo, $hash ) = explode( '=', $_SERVER[ 'HTTP_X_JSXC_SIGNATURE' ], 2 ) + array( '', '' ); -if ( ! in_array( $algo, hash_algos(), TRUE ) ) - abort( "Hash algorithm '$algo' is not supported." ); - -// check if the key is valid -$rawPost = file_get_contents( 'php://input' ); -if ( $hash !== hash_hmac( $algo, $rawPost, $apiSecret ) ) - abort( 'Signature does not match.' ); - -switch($_POST['operation']) { - case 'auth': - checkPassword(); - break; - case 'isuser': - isUser(); - break; - case 'sharedroster': - sharedRoster(); - break; - default: - abort( "Unsupported operation."); -} diff --git a/appinfo/application.php b/appinfo/application.php index 2ce4a6d7..c2244380 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -3,6 +3,8 @@ namespace OCA\OJSXC\AppInfo; use OCA\OJSXC\Controller\SettingsController; +use OCA\OJSXC\Controller\ExternalApiController; +use OCA\OJSXC\Middleware\ExternalApiMiddleware; use OCA\OJSXC\Command\RefreshRoster; use OCA\OJSXC\Controller\HttpBindController; use OCA\OJSXC\Db\IQRosterPushMapper; @@ -74,6 +76,29 @@ public function __construct(array $urlParams=array()){ ); }); + $container->registerService('ExternalApiController', function(IContainer $c) { + return new ExternalApiController( + $c->query('AppName'), + $c->query('Request'), + $c->query('UserManager'), + $c->query('GroupManager'), + $c->query('Logger') + ); + }); + + /** + * Middleware + */ + + $container->registerService('ExternalApiMiddleware', function(IContainer $c) { + return new ExternalApiMiddleware( + $c->query('Request'), + $c->query('OCP\IConfig'), + file_get_contents('php://input') + ); + }); + $container->registerMiddleware('ExternalApiMiddleware'); + /** * Database Layer */ diff --git a/appinfo/routes.php b/appinfo/routes.php index 7bf684c7..8f836eb8 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -34,6 +34,10 @@ array('name' => 'settings#setUser', 'url' => '/settings/user', 'verb' => 'POST'), array('name' => 'settings#getIceServers', 'url' => '/settings/iceServers', 'verb' => 'GET'), array('name' => 'settings#getUsers', 'url' => '/settings/users', 'verb' => 'GET'), + array('name' => 'externalApi#index', 'url' => '/ajax/externalApi.php', 'verb' => 'POST'), + array('name' => 'externalApi#checkPassword', 'url' => '/externalApi/checkPassword', 'verb' => 'POST'), + array('name' => 'externalApi#isUser', 'url' => '/externalApi/isUser', 'verb' => 'POST'), + array('name' => 'externalApi#sharedRoster', 'url' => '/externalApi/sharedRoster', 'verb' => 'POST'), ) )); ?> diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php new file mode 100644 index 00000000..90a9cda1 --- /dev/null +++ b/lib/Controller/ExternalApiController.php @@ -0,0 +1,173 @@ +userManager = $userManager; + $this->groupManager = $groupManager; + + $this->userSession = \OC::$server->getUserSession(); + + $this->logger = $logger; + } + + /** + * @NoAdminRequired + * @PublicPage + * @NoCSRFRequired + */ + public function index($operation) + { + switch($operation) { + case 'auth': + return checkPassword( + $this->request->getParam('username'), + $this->request->getParam('password'), + $this->request->getParam('domain') + ); + break; + case 'isuser': + return isUser( + $this->request->getParam('username'), + $this->request->getParam('domain') + ); + break; + case 'sharedroster': + return sharedRoster( + $this->request->getParam('username'), + $this->request->getParam('domain') + ); + break; + default: + throw new \Exception( 'Unsupported operation.'); + } + } + + /** + * @NoAdminRequired + * @PublicPage + * @NoCSRFRequired + */ + public function checkPassword($username = '', $password = '', $domain = '') { + $currentUser = null; + + $this->logger->info('ExAPI: Check password for user: '.$username.'@'.$domain); + + if(!empty($password) && !empty($username)) { + if(!empty($domain)) { + $currentUser = $this->userManager->checkPassword($username . '@' . $domain, $password); + } + if($currentUser === null) { + $currentUser = $this->userManager->checkPassword($username, $password); + } + } + + if (!$currentUser) { + return array( + 'result' => 'noauth', + ); + } + + $data = array(); + $data ['uid'] = $currentUser->getUID(); + + return array( + 'result' => 'success', + 'data' => $data, + ); + } + + /** + * @NoAdminRequired + * @PublicPage + * @NoCSRFRequired + */ + public function isUser($username = '', $domain = '') { + $this->logger->info('ExAPI: Check if "'.$username.'@'.$domain.'" exists'); + + $isUser = false; + + if(!empty($username)) { + if(!empty($domain)) { + $isUser = $this->userManager->userExists($username . '@' . $domain); + } + if(!$isUser) { + $isUser = $this->userManager->userExists($username); + } + } + + return array( + 'result' => 'success', + 'data' => array( + 'isUser' => $isUser + ) + ); + } + + /** + * @NoAdminRequired + * @PublicPage + * @NoCSRFRequired + */ + public function sharedRoster($username = '', $domain = '') { + if(!empty($username)) { + if(!empty($domain)) { + $username .= '@' . $domain; + } + } else { + throw new \Exception('No username provided'); + } + + $roster = []; + + $userGroups = $this->groupManager->getUserIdGroups($username); + + foreach($userGroups as $userGroup) { + foreach($userGroup->getUsers() as $user) { + $uid = $user->getUID(); + + if(!$roster[$uid]) { + $roster[$uid] = [ + 'name' => $user->getDisplayName(), + 'groups' => [] + ]; + } + + $roster[$uid]['groups'][] = $userGroup->getDisplayName(); + } + } + + return array( + 'result' => 'success', + 'data' => array( + 'sharedRoster' => $roster + ) + ); + } +} diff --git a/lib/Exceptions/SecurityException.php b/lib/Exceptions/SecurityException.php new file mode 100644 index 00000000..541efe81 --- /dev/null +++ b/lib/Exceptions/SecurityException.php @@ -0,0 +1,5 @@ +request = $request; + $this->config = $config; + $this->rawPost = $rawPost; + } + + public function beforeController($controller, $methodName) + { + $controllerName = get_class($controller); + + if ($controllerName !== 'OCA\OJSXC\Controller\ExternalApiController') { + return; + } + + $apiSecret = $this->config->getAppValue('ojsxc', 'apiSecret'); + $jsxcSignatureHeader = $this->request->getHeader('X-JSXC-SIGNATURE'); + + // check if we have a signature + if (! isset($jsxcSignatureHeader)) { + throw new SecurityException('HTTP header "X-JSXC-Signature" is missing.'); + } elseif (! extension_loaded('hash')) { + throw new SecurityException('Missing "hash" extension to check the secret code validity.'); + } elseif (! $apiSecret) { + throw new SecurityException('Missing secret.'); + } + + // check if the algo is supported + list($algo, $hash) = explode('=', $jsxcSignatureHeader, 2) + array( '', '' ); + if (! in_array($algo, hash_algos(), true)) { + throw new SecurityException("Hash algorithm '$algo' is not supported."); + } + + // check if the key is valid + if ($hash !== hash_hmac($algo, $this->rawPost, $apiSecret)) { + throw new SecurityException('Signature does not match.'); + } + } + + public function afterException($controller, $methodName, \Exception $exception) + { + //@TODO filter exception, because this will fetch all exceptions from all controllers in ojsxc + return new JSONResponse(array( + 'result' => 'error', + 'data' => array( + 'msg' => $exception->getMessage() + ) + ), Http::STATUS_INTERNAL_SERVER_ERROR); + } +} From 2bbae434a039d3beefd5274f4e659368fe97f5dc Mon Sep 17 00:00:00 2001 From: sualko Date: Fri, 21 Jul 2017 17:02:10 +0200 Subject: [PATCH 19/35] remove old routes --- appinfo/routes.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 8f836eb8..f2e3f9d5 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -10,21 +10,6 @@ use \OCA\OJSXC\AppInfo\Application; -$this->create('ojsxc_ajax_getturncredentials', 'ajax/getTurnCredentials.php') - ->actionInclude('ojsxc/ajax/getTurnCredentials.php'); - -$this->create('ojsxc_ajax_setadminsettings', 'ajax/setAdminSettings.php') - ->actionInclude('ojsxc/ajax/setAdminSettings.php'); - -$this->create('ojsxc_ajax_setUserSettings', 'ajax/setUserSettings.php') - ->actionInclude('ojsxc/ajax/setUserSettings.php'); - -$this->create('ojsxc_ajax_getUsers', 'ajax/getUsers.php') - ->actionInclude('ojsxc/ajax/getUsers.php'); - -$this->create('ojsxc_ajax_externalApi', 'ajax/externalApi.php') - ->actionInclude('ojsxc/ajax/externalApi.php'); - $application = new Application(); $application->registerRoutes($this, array( 'routes' => array( From 691979f52545499ef12a6a70f06205f8650b6ce4 Mon Sep 17 00:00:00 2001 From: sualko Date: Sat, 22 Jul 2017 22:41:03 +0200 Subject: [PATCH 20/35] update phpunit to 5.7 --- composer.json | 2 +- composer.lock | 662 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 513 insertions(+), 151 deletions(-) diff --git a/composer.json b/composer.json index c44af99d..e82d7ed3 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,6 @@ "sabre/xml": "^1.2" }, "require-dev": { - "phpunit/phpunit": "4.8.*" + "phpunit/phpunit": "^5.7" } } diff --git a/composer.lock b/composer.lock index f9a49458..8ab8aa01 100644 --- a/composer.lock +++ b/composer.lock @@ -4,29 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ddad5689988b7ba558f7d088df1fe91c", - "content-hash": "0526657850896a1f6e4ae442c9ed7015", + "hash": "26380469f11f2ef38b5e992029d50c81", + "content-hash": "a7f1c4a771a539e51b63a32825b60261", "packages": [ { "name": "sabre/uri", - "version": "1.0.1", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/fruux/sabre-uri.git", - "reference": "6bae7efdd9dfcfdb3edfc4362741e59ce4b64f42" + "reference": "a42126042c7dcb53e2978dadb6d22574d1359b4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/6bae7efdd9dfcfdb3edfc4362741e59ce4b64f42", - "reference": "6bae7efdd9dfcfdb3edfc4362741e59ce4b64f42", + "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/a42126042c7dcb53e2978dadb6d22574d1359b4c", + "reference": "a42126042c7dcb53e2978dadb6d22574d1359b4c", "shasum": "" }, "require": { - "php": ">=5.4.7" + "php": ">=7" }, "require-dev": { - "phpunit/phpunit": "*", - "sabre/cs": "~0.0.1" + "phpunit/phpunit": "^6.0", + "sabre/cs": "~1.0.0" }, "type": "library", "autoload": { @@ -56,20 +56,20 @@ "uri", "url" ], - "time": "2015-04-29 03:47:26" + "time": "2017-02-20 20:02:35" }, { "name": "sabre/xml", - "version": "1.3.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/fruux/sabre-xml.git", - "reference": "420400f36655d79894fae8ce970516a71ea8f5f5" + "reference": "59b20e5bbace9912607481634f97d05a776ffca7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruux/sabre-xml/zipball/420400f36655d79894fae8ce970516a71ea8f5f5", - "reference": "420400f36655d79894fae8ce970516a71ea8f5f5", + "url": "https://api.github.com/repos/fruux/sabre-xml/zipball/59b20e5bbace9912607481634f97d05a776ffca7", + "reference": "59b20e5bbace9912607481634f97d05a776ffca7", "shasum": "" }, "require": { @@ -77,12 +77,12 @@ "ext-xmlreader": "*", "ext-xmlwriter": "*", "lib-libxml": ">=2.6.20", - "php": ">=5.4.1", - "sabre/uri": "~1.0" + "php": ">=5.5.5", + "sabre/uri": ">=1.0,<3.0.0" }, "require-dev": { "phpunit/phpunit": "*", - "sabre/cs": "~0.0.2" + "sabre/cs": "~1.0.0" }, "type": "library", "autoload": { @@ -119,7 +119,7 @@ "dom", "xml" ], - "time": "2015-12-29 20:51:22" + "time": "2016-10-09 22:57:52" } ], "packages-dev": [ @@ -177,39 +177,178 @@ ], "time": "2015-06-14 21:17:01" }, + { + "name": "myclabs/deep-copy", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "doctrine/collections": "1.*", + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "homepage": "https://github.com/myclabs/DeepCopy", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-04-12 18:52:22" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27 11:43:31" + }, { "name": "phpdocumentor/reflection-docblock", - "version": "2.0.4", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/46f7e8bb075036c92695b15a1ddb6971c751e585", + "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-07-15 11:38:20" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ + "psr-4": { + "phpDocumentor\\Reflection\\": [ "src/" ] } @@ -221,37 +360,40 @@ "authors": [ { "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "email": "me@mikevanriel.com" } ], - "time": "2015-02-03 12:10:50" + "time": "2017-07-14 14:27:02" }, { "name": "phpspec/prophecy", - "version": "v1.5.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", - "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", - "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1" + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "~2.0" + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -284,43 +426,44 @@ "spy", "stub" ], - "time": "2015-08-13 10:07:40" + "time": "2017-03-02 20:05:34" }, { "name": "phpunit/php-code-coverage", - "version": "2.2.4", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^5.6 || ^7.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", + "sebastian/environment": "^1.3.2 || ^2.0", + "sebastian/version": "^1.0 || ^2.0" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.5.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -346,20 +489,20 @@ "testing", "xunit" ], - "time": "2015-10-06 15:47:00" + "time": "2017-04-02 07:44:40" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.1", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", - "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", "shasum": "" }, "require": { @@ -393,7 +536,7 @@ "filesystem", "iterator" ], - "time": "2015-06-21 13:08:43" + "time": "2016-10-03 07:40:28" }, { "name": "phpunit/php-text-template", @@ -438,22 +581,30 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.7", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", - "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -475,20 +626,20 @@ "keywords": [ "timer" ], - "time": "2015-06-21 08:01:12" + "time": "2017-02-26 11:10:40" }, { "name": "phpunit/php-token-stream", - "version": "1.4.8", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", "shasum": "" }, "require": { @@ -524,44 +675,54 @@ "keywords": [ "tokenizer" ], - "time": "2015-09-15 10:49:45" + "time": "2017-02-27 10:12:30" }, { "name": "phpunit/phpunit", - "version": "4.8.21", + "version": "5.7.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ea76b17bced0500a28098626b84eda12dbcf119c" + "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c", - "reference": "ea76b17bced0500a28098626b84eda12dbcf119c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b91adfb64264ddec5a2dee9851f354aa66327db", + "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "~1.3", + "php": "^5.6 || ^7.0", + "phpspec/prophecy": "^1.6.2", + "phpunit/php-code-coverage": "^4.0.4", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": ">=1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.1", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "^3.2", + "sebastian/comparator": "^1.2.4", + "sebastian/diff": "^1.4.3", + "sebastian/environment": "^1.3.4 || ^2.0", + "sebastian/exporter": "~2.0", + "sebastian/global-state": "^1.1", + "sebastian/object-enumerator": "~2.0", + "sebastian/resource-operations": "~1.0", + "sebastian/version": "~1.0.3|~2.0", "symfony/yaml": "~2.1|~3.0" }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2" + }, + "require-dev": { + "ext-pdo": "*" + }, "suggest": { + "ext-xdebug": "*", "phpunit/php-invoker": "~1.1" }, "bin": [ @@ -570,7 +731,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.8.x-dev" + "dev-master": "5.7.x-dev" } }, "autoload": { @@ -596,30 +757,33 @@ "testing", "xunit" ], - "time": "2015-12-12 07:45:58" + "time": "2017-06-21 08:11:54" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" + "php": "^5.6 || ^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^1.2 || ^2.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^5.4" }, "suggest": { "ext-soap": "*" @@ -627,7 +791,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "3.2.x-dev" } }, "autoload": { @@ -652,26 +816,71 @@ "mock", "xunit" ], - "time": "2015-10-02 06:51:40" + "time": "2017-06-30 09:13:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04 06:30:41" }, { "name": "sebastian/comparator", - "version": "1.2.0", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", "shasum": "" }, "require": { "php": ">=5.3.3", "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2" + "sebastian/exporter": "~1.2 || ~2.0" }, "require-dev": { "phpunit/phpunit": "~4.4" @@ -716,27 +925,27 @@ "compare", "equality" ], - "time": "2015-07-26 15:48:44" + "time": "2017-01-29 09:50:25" }, { "name": "sebastian/diff", - "version": "1.4.1", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { @@ -768,32 +977,32 @@ "keywords": [ "diff" ], - "time": "2015-12-08 07:14:41" + "time": "2017-05-22 07:24:03" }, { "name": "sebastian/environment", - "version": "1.3.3", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6e7133793a8e5a5714a551a8324337374be209df" + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", - "reference": "6e7133793a8e5a5714a551a8324337374be209df", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -818,33 +1027,34 @@ "environment", "hhvm" ], - "time": "2015-12-02 08:37:27" + "time": "2016-11-26 07:53:53" }, { "name": "sebastian/exporter", - "version": "1.2.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", "shasum": "" }, "require": { "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" + "sebastian/recursion-context": "~2.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -884,7 +1094,7 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2016-11-19 08:54:04" }, { "name": "sebastian/global-state", @@ -937,18 +1147,64 @@ ], "time": "2015-10-12 03:26:01" }, + { + "name": "sebastian/object-enumerator", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-02-18 15:18:39" + }, { "name": "sebastian/recursion-context", - "version": "1.0.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "913401df809e99e4f47b27cdd781f4a258d58791" + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", - "reference": "913401df809e99e4f47b27cdd781f4a258d58791", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", "shasum": "" }, "require": { @@ -960,7 +1216,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -988,23 +1244,73 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-11-11 19:50:13" + "time": "2016-11-19 07:33:16" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28 20:34:47" }, { "name": "sebastian/version", - "version": "1.0.6", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, + "require": { + "php": ">=5.6" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -1023,29 +1329,35 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21 13:59:46" + "time": "2016-10-03 07:35:21" }, { "name": "symfony/yaml", - "version": "v2.8.2", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8" + "reference": "1f93a8d19b8241617f5074a123e282575b821df8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/34c8a4b51e751e7ea869b8262f883d008a2b81b8", - "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8", + "url": "https://api.github.com/repos/symfony/yaml/zipball/1f93a8d19b8241617f5074a123e282575b821df8", + "reference": "1f93a8d19b8241617f5074a123e282575b821df8", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -1072,7 +1384,57 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-01-13 10:28:07" + "time": "2017-06-15 12:58:50" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23 20:04:58" } ], "aliases": [], From 384fad1e2bbe2af32d799dc41cb3f661f051aaf9 Mon Sep 17 00:00:00 2001 From: sualko Date: Sat, 22 Jul 2017 22:45:06 +0200 Subject: [PATCH 21/35] prepare ExternalApiMiddleware for tests - inject raw request body - use SignatureProtectedApiController to require signature in header --- appinfo/application.php | 10 +++++++- lib/Controller/ExternalApiController.php | 24 ++----------------- .../SignatureProtectedApiController.php | 9 +++++++ lib/Middleware/ExternalApiMiddleware.php | 14 +++++------ lib/RawRequest.php | 9 +++++++ 5 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 lib/Controller/SignatureProtectedApiController.php create mode 100644 lib/RawRequest.php diff --git a/appinfo/application.php b/appinfo/application.php index c2244380..11b136a4 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -18,6 +18,7 @@ use OCA\OJSXC\StanzaHandlers\Message; use OCA\OJSXC\StanzaHandlers\Presence; use OCA\OJSXC\StanzaLogger; +use OCA\OJSXC\RawRequest; use OCP\AppFramework\App; use OCA\OJSXC\ILock; use OCA\OJSXC\DbLock; @@ -94,7 +95,7 @@ public function __construct(array $urlParams=array()){ return new ExternalApiMiddleware( $c->query('Request'), $c->query('OCP\IConfig'), - file_get_contents('php://input') + $c->query('RawRequest') ); }); $container->registerMiddleware('ExternalApiMiddleware'); @@ -221,6 +222,13 @@ public function __construct(array $urlParams=array()){ return strtolower($c->query('UserId')); }); + /** + * Raw request body + */ + $container->registerService('RawRequest', function($c) { + return new RawRequest(); + }); + } /** diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php index 90a9cda1..9067d5f4 100644 --- a/lib/Controller/ExternalApiController.php +++ b/lib/Controller/ExternalApiController.php @@ -2,7 +2,7 @@ namespace OCA\OJSXC\Controller; -use OCP\AppFramework\Controller; +use OCP\AppFramework\ApiController; use OCP\IConfig; use OCP\IRequest; use OCP\IUserManager; @@ -11,7 +11,7 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http; -class ExternalApiController extends Controller +class ExternalApiController extends SignatureProtectedApiController { private $userManager; @@ -37,11 +37,6 @@ public function __construct($appName, $this->logger = $logger; } - /** - * @NoAdminRequired - * @PublicPage - * @NoCSRFRequired - */ public function index($operation) { switch($operation) { @@ -69,11 +64,6 @@ public function index($operation) } } - /** - * @NoAdminRequired - * @PublicPage - * @NoCSRFRequired - */ public function checkPassword($username = '', $password = '', $domain = '') { $currentUser = null; @@ -103,11 +93,6 @@ public function checkPassword($username = '', $password = '', $domain = '') { ); } - /** - * @NoAdminRequired - * @PublicPage - * @NoCSRFRequired - */ public function isUser($username = '', $domain = '') { $this->logger->info('ExAPI: Check if "'.$username.'@'.$domain.'" exists'); @@ -130,11 +115,6 @@ public function isUser($username = '', $domain = '') { ); } - /** - * @NoAdminRequired - * @PublicPage - * @NoCSRFRequired - */ public function sharedRoster($username = '', $domain = '') { if(!empty($username)) { if(!empty($domain)) { diff --git a/lib/Controller/SignatureProtectedApiController.php b/lib/Controller/SignatureProtectedApiController.php new file mode 100644 index 00000000..0d2edace --- /dev/null +++ b/lib/Controller/SignatureProtectedApiController.php @@ -0,0 +1,9 @@ +request = $request; $this->config = $config; - $this->rawPost = $rawPost; + $this->rawRequest = $rawRequest; } public function beforeController($controller, $methodName) { - $controllerName = get_class($controller); - - if ($controllerName !== 'OCA\OJSXC\Controller\ExternalApiController') { + if (!$controller instanceof SignatureProtectedApiController) { return; } @@ -51,7 +51,7 @@ public function beforeController($controller, $methodName) } // check if the key is valid - if ($hash !== hash_hmac($algo, $this->rawPost, $apiSecret)) { + if ($hash !== hash_hmac($algo, $this->rawRequest->get(), $apiSecret)) { throw new SecurityException('Signature does not match.'); } } diff --git a/lib/RawRequest.php b/lib/RawRequest.php new file mode 100644 index 00000000..0ebe9c00 --- /dev/null +++ b/lib/RawRequest.php @@ -0,0 +1,9 @@ + Date: Sat, 22 Jul 2017 22:47:54 +0200 Subject: [PATCH 22/35] add tests for ExternalApiMiddleware --- .../Middleware/ExternalApiMiddlewareTest.php | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 tests/unit/Middleware/ExternalApiMiddlewareTest.php diff --git a/tests/unit/Middleware/ExternalApiMiddlewareTest.php b/tests/unit/Middleware/ExternalApiMiddlewareTest.php new file mode 100644 index 00000000..5af16ae9 --- /dev/null +++ b/tests/unit/Middleware/ExternalApiMiddlewareTest.php @@ -0,0 +1,151 @@ +request = $this->createMock(IRequest::class); + $this->config = $this->createMock(IConfig::class); + $this->rawRequest = $this->createMock(RawRequest::class); + + $this->externalApiMiddleware = new ExternalApiMiddleware( + $this->request, + $this->config, + $this->rawRequest + ); + } + + public function testBeforeControllerWithoutHeader() { + $this->request + ->expects($this->once()) + ->method('getHeader') + ->with('X-JSXC-SIGNATURE') + ->willReturn(null); + + $this->expectException(SecurityException::class); + $this->expectExceptionMessage('HTTP header "X-JSXC-Signature" is missing.'); + + $controller = $this->createMock(SignatureProtectedApiController::class); + $this->externalApiMiddleware->beforeController($controller, 'someMethod'); + } + + public function testBeforeControllerWithoutSecret() { + $this->request + ->expects($this->once()) + ->method('getHeader') + ->with('X-JSXC-SIGNATURE') + ->willReturn('foo=bar'); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('ojsxc', 'apiSecret') + ->willReturn(null); + + $this->expectException(SecurityException::class); + $this->expectExceptionMessage('Missing secret.'); + + $controller = $this->createMock(SignatureProtectedApiController::class); + $this->externalApiMiddleware->beforeController($controller, 'someMethod'); + } + + public function testBeforeControllerWithUnsupportedAlgo() { + $this->request + ->expects($this->once()) + ->method('getHeader') + ->with('X-JSXC-SIGNATURE') + ->willReturn('foo=bar'); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('ojsxc', 'apiSecret') + ->willReturn('secret'); + + $this->expectException(SecurityException::class); + $this->expectExceptionMessage('Hash algorithm \'foo\' is not supported.'); + + $controller = $this->createMock(SignatureProtectedApiController::class); + $this->externalApiMiddleware->beforeController($controller, 'someMethod'); + } + + public function testBeforeControllerWithInvalidHeaderFormat() { + $this->request + ->expects($this->once()) + ->method('getHeader') + ->with('X-JSXC-SIGNATURE') + ->willReturn('foobar'); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('ojsxc', 'apiSecret') + ->willReturn('secret'); + + $this->expectException(SecurityException::class); + $this->expectExceptionMessage('Hash algorithm \'foobar\' is not supported.'); + + $controller = $this->createMock(SignatureProtectedApiController::class); + $this->externalApiMiddleware->beforeController($controller, 'someMethod'); + } + + public function testBeforeControllerWithInvalidHeader() { + $this->request + ->expects($this->once()) + ->method('getHeader') + ->with('X-JSXC-SIGNATURE') + ->willReturn('sha1=foobar'); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('ojsxc', 'apiSecret') + ->willReturn('secret'); + $this->rawRequest + ->expects($this->once()) + ->method('get') + ->willReturn('asdf'); + + $this->expectException(SecurityException::class); + $this->expectExceptionMessage('Signature does not match.'); + + $controller = $this->createMock(SignatureProtectedApiController::class); + $this->externalApiMiddleware->beforeController($controller, 'someMethod'); + } + + public function testBeforeControllerWithValidHeader() { + $algo = 'sha1'; + $apiSecret = 'secret'; + $rawRequestBody = 'rawRequestBody'; + $hash = hash_hmac($algo, $rawRequestBody, $apiSecret); + + $this->request + ->expects($this->once()) + ->method('getHeader') + ->with('X-JSXC-SIGNATURE') + ->willReturn($algo.'='.$hash); + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('ojsxc', 'apiSecret') + ->willReturn($apiSecret); + $this->rawRequest + ->expects($this->once()) + ->method('get') + ->willReturn($rawRequestBody); + + $controller = $this->createMock(SignatureProtectedApiController::class); + $this->externalApiMiddleware->beforeController($controller, 'someMethod'); + } +} From 401dfa3d08d730153d2f2bcc47e00168f6ccbce7 Mon Sep 17 00:00:00 2001 From: sualko Date: Sat, 22 Jul 2017 23:37:15 +0200 Subject: [PATCH 23/35] force sabre/uri ~1.0 as dependency to support php5.6 --- composer.json | 3 ++- composer.lock | 69 +++++++++++++++++++++++++-------------------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/composer.json b/composer.json index e82d7ed3..cdf2998f 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "require": { - "sabre/xml": "^1.2" + "sabre/xml": "^1.2", + "sabre/uri": "~1.0" }, "require-dev": { "phpunit/phpunit": "^5.7" diff --git a/composer.lock b/composer.lock index 8ab8aa01..b1fff030 100644 --- a/composer.lock +++ b/composer.lock @@ -4,28 +4,27 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "26380469f11f2ef38b5e992029d50c81", - "content-hash": "a7f1c4a771a539e51b63a32825b60261", + "content-hash": "124d1797b36e8cf0842dfd26ca205df3", "packages": [ { "name": "sabre/uri", - "version": "2.1.1", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/fruux/sabre-uri.git", - "reference": "a42126042c7dcb53e2978dadb6d22574d1359b4c" + "reference": "ada354d83579565949d80b2e15593c2371225e61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/a42126042c7dcb53e2978dadb6d22574d1359b4c", - "reference": "a42126042c7dcb53e2978dadb6d22574d1359b4c", + "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/ada354d83579565949d80b2e15593c2371225e61", + "reference": "ada354d83579565949d80b2e15593c2371225e61", "shasum": "" }, "require": { - "php": ">=7" + "php": ">=5.4.7" }, "require-dev": { - "phpunit/phpunit": "^6.0", + "phpunit/phpunit": ">=4.0,<6.0", "sabre/cs": "~1.0.0" }, "type": "library", @@ -56,7 +55,7 @@ "uri", "url" ], - "time": "2017-02-20 20:02:35" + "time": "2017-02-20T19:59:28+00:00" }, { "name": "sabre/xml", @@ -119,7 +118,7 @@ "dom", "xml" ], - "time": "2016-10-09 22:57:52" + "time": "2016-10-09T22:57:52+00:00" } ], "packages-dev": [ @@ -175,7 +174,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "myclabs/deep-copy", @@ -217,7 +216,7 @@ "object", "object graph" ], - "time": "2017-04-12 18:52:22" + "time": "2017-04-12T18:52:22+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -271,7 +270,7 @@ "reflection", "static analysis" ], - "time": "2015-12-27 11:43:31" + "time": "2015-12-27T11:43:31+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -316,7 +315,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-07-15 11:38:20" + "time": "2017-07-15T11:38:20+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -363,7 +362,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14 14:27:02" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", @@ -426,7 +425,7 @@ "spy", "stub" ], - "time": "2017-03-02 20:05:34" + "time": "2017-03-02T20:05:34+00:00" }, { "name": "phpunit/php-code-coverage", @@ -489,7 +488,7 @@ "testing", "xunit" ], - "time": "2017-04-02 07:44:40" + "time": "2017-04-02T07:44:40+00:00" }, { "name": "phpunit/php-file-iterator", @@ -536,7 +535,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03 07:40:28" + "time": "2016-10-03T07:40:28+00:00" }, { "name": "phpunit/php-text-template", @@ -577,7 +576,7 @@ "keywords": [ "template" ], - "time": "2015-06-21 13:50:34" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", @@ -626,7 +625,7 @@ "keywords": [ "timer" ], - "time": "2017-02-26 11:10:40" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", @@ -675,7 +674,7 @@ "keywords": [ "tokenizer" ], - "time": "2017-02-27 10:12:30" + "time": "2017-02-27T10:12:30+00:00" }, { "name": "phpunit/phpunit", @@ -757,7 +756,7 @@ "testing", "xunit" ], - "time": "2017-06-21 08:11:54" + "time": "2017-06-21T08:11:54+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -816,7 +815,7 @@ "mock", "xunit" ], - "time": "2017-06-30 09:13:00" + "time": "2017-06-30T09:13:00+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -861,7 +860,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04 06:30:41" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -925,7 +924,7 @@ "compare", "equality" ], - "time": "2017-01-29 09:50:25" + "time": "2017-01-29T09:50:25+00:00" }, { "name": "sebastian/diff", @@ -977,7 +976,7 @@ "keywords": [ "diff" ], - "time": "2017-05-22 07:24:03" + "time": "2017-05-22T07:24:03+00:00" }, { "name": "sebastian/environment", @@ -1027,7 +1026,7 @@ "environment", "hhvm" ], - "time": "2016-11-26 07:53:53" + "time": "2016-11-26T07:53:53+00:00" }, { "name": "sebastian/exporter", @@ -1094,7 +1093,7 @@ "export", "exporter" ], - "time": "2016-11-19 08:54:04" + "time": "2016-11-19T08:54:04+00:00" }, { "name": "sebastian/global-state", @@ -1145,7 +1144,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12 03:26:01" + "time": "2015-10-12T03:26:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -1191,7 +1190,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18 15:18:39" + "time": "2017-02-18T15:18:39+00:00" }, { "name": "sebastian/recursion-context", @@ -1244,7 +1243,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19 07:33:16" + "time": "2016-11-19T07:33:16+00:00" }, { "name": "sebastian/resource-operations", @@ -1286,7 +1285,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28 20:34:47" + "time": "2015-07-28T20:34:47+00:00" }, { "name": "sebastian/version", @@ -1329,7 +1328,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03 07:35:21" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/yaml", @@ -1384,7 +1383,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-06-15 12:58:50" + "time": "2017-06-15T12:58:50+00:00" }, { "name": "webmozart/assert", @@ -1434,7 +1433,7 @@ "check", "validate" ], - "time": "2016-11-23 20:04:58" + "time": "2016-11-23T20:04:58+00:00" } ], "aliases": [], From 56a3a160a9db42d23a544ddd91a86deb87f70b38 Mon Sep 17 00:00:00 2001 From: sualko Date: Sun, 23 Jul 2017 19:33:46 +0200 Subject: [PATCH 24/35] add tests for externalApiController --- lib/Controller/ExternalApiController.php | 7 +- .../controller/ExternalApiControllerTest.php | 318 ++++++++++++++++++ 2 files changed, 321 insertions(+), 4 deletions(-) create mode 100644 tests/unit/controller/ExternalApiControllerTest.php diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php index 9067d5f4..00be017b 100644 --- a/lib/Controller/ExternalApiController.php +++ b/lib/Controller/ExternalApiController.php @@ -2,7 +2,6 @@ namespace OCA\OJSXC\Controller; -use OCP\AppFramework\ApiController; use OCP\IConfig; use OCP\IRequest; use OCP\IUserManager; @@ -101,8 +100,7 @@ public function isUser($username = '', $domain = '') { if(!empty($username)) { if(!empty($domain)) { $isUser = $this->userManager->userExists($username . '@' . $domain); - } - if(!$isUser) { + } else { $isUser = $this->userManager->userExists($username); } } @@ -125,8 +123,9 @@ public function sharedRoster($username = '', $domain = '') { } $roster = []; + $user = $this->userManager->get($username); - $userGroups = $this->groupManager->getUserIdGroups($username); + $userGroups = $this->groupManager->getUserGroups($user); foreach($userGroups as $userGroup) { foreach($userGroup->getUsers() as $user) { diff --git a/tests/unit/controller/ExternalApiControllerTest.php b/tests/unit/controller/ExternalApiControllerTest.php new file mode 100644 index 00000000..61d2a5b2 --- /dev/null +++ b/tests/unit/controller/ExternalApiControllerTest.php @@ -0,0 +1,318 @@ +request = $this->createMock(IRequest::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->logger = $this->createMock(ILogger::class); + $this->user = $this->createMock(IUser::class); + + $this->externalApiController = new ExternalApiController( + 'ojsxc', + $this->request, + $this->userManager, + $this->groupManager, + $this->logger + ); + } + + public function testSignatureProtected() { + $this->assertInstanceOf(SignatureProtectedApiController::class, $this->externalApiController); + } + + public function testCheckPasswordWithInvalidParams() { + $this->userManager + ->expects($this->once()) + ->method('checkPassword') + ->with('foo', 'bar') + ->willReturn(false); + + $return = $this->externalApiController->checkPassword('foo', 'bar'); + + $this->assertEquals('noauth', $return['result']); + } + + public function testCheckPasswordWithInvalidParamsAndDomain() { + $this->userManager + ->expects($this->once()) + ->method('checkPassword') + ->with('foo@localhost', 'bar') + ->willReturn(false); + + $return = $this->externalApiController->checkPassword('foo', 'bar', 'localhost'); + + $this->assertEquals('noauth', $return['result']); + } + + public function testCheckPasswordWithValidParams() { + $uid = 'foo'; + $this->user + ->expects($this->once()) + ->method('getUID') + ->willReturn($uid); + $this->userManager + ->expects($this->once()) + ->method('checkPassword') + ->with('foo', 'bar') + ->willReturn($this->user); + + $return = $this->externalApiController->checkPassword('foo', 'bar'); + + $this->assertEquals('success', $return['result']); + $this->assertEquals($uid, $return['data']['uid']); + } + + public function testCheckPasswordWithValidParamsAndDomain() { + $uid = 'foo'; + $this->user + ->expects($this->once()) + ->method('getUID') + ->willReturn($uid); + $this->userManager + ->expects($this->once()) + ->method('checkPassword') + ->with('foo@localhost', 'bar') + ->willReturn($this->user); + + $return = $this->externalApiController->checkPassword('foo', 'bar', 'localhost'); + + $this->assertEquals('success', $return['result']); + $this->assertEquals($uid, $return['data']['uid']); + } + + public function testIsUserFail() { + $this->userManager + ->expects($this->once()) + ->method('userExists') + ->with('foo') + ->willReturn(false); + + $return = $this->externalApiController->isUser('foo'); + + $this->assertEquals('success', $return['result']); + $this->assertEquals(false, $return['data']['isUser']); + } + + public function testIsUserFailWithDomain() { + $this->userManager + ->expects($this->once()) + ->method('userExists') + ->with('foo@localhost') + ->willReturn(false); + + $return = $this->externalApiController->isUser('foo', 'localhost'); + + $this->assertEquals('success', $return['result']); + $this->assertEquals(false, $return['data']['isUser']); + } + + public function testIsUserSuccess() { + $this->userManager + ->expects($this->once()) + ->method('userExists') + ->with('foo') + ->willReturn(true); + + $return = $this->externalApiController->isUser('foo'); + + $this->assertEquals('success', $return['result']); + $this->assertEquals(true, $return['data']['isUser']); + } + + public function testIsUserSuccessWithDomain() { + $this->userManager + ->expects($this->once()) + ->method('userExists') + ->with('foo@localhost') + ->willReturn(true); + + $return = $this->externalApiController->isUser('foo', 'localhost'); + + $this->assertEquals('success', $return['result']); + $this->assertEquals(true, $return['data']['isUser']); + } + + public function testSharedRosterWithoutUsername() { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('No username provided'); + + $this->externalApiController->sharedRoster(); + } + + public function testSharedRosterNoGroups() { + $user = $this->createUserMock('foobar'); + + $this->userManager + ->expects($this->once()) + ->method('get') + ->with($user->getUID()) + ->willReturn($user); + + $this->groupManager + ->expects($this->once()) + ->method('getUserGroups') + ->with($user) + ->willReturn([]); + + $return = $this->externalApiController->sharedRoster($user->getUID()); + + $this->assertEquals('success', $return['result']); + $this->assertEquals([], $return['data']['sharedRoster']); + } + + public function testSharedRosterMultipleDistinctGroups() { + $user = $this->createUserMock('foobar'); + + $this->userManager + ->expects($this->once()) + ->method('get') + ->with($user->getUID()) + ->willReturn($user); + + $this->groupManager + ->expects($this->once()) + ->method('getUserGroups') + ->with($user) + ->willReturn([ + $this->createGroupMock('Group1', ['Foo Bar', 'John Doo', 'user42']), + $this->createGroupMock('Group2', ['Fritz', 'John', 'Eve']) + ]); + + $expectedResult = [ + 'Foo-Bar' => [ + 'name' => 'Foo Bar', + 'groups' => ['Group1'] + ], + 'John-Doo' => [ + 'name' => 'John Doo', + 'groups' => ['Group1'] + ], + 'user42' => [ + 'name' => 'user42', + 'groups' => ['Group1'] + ], + 'Fritz' => [ + 'name' => 'Fritz', + 'groups' => ['Group2'] + ], + 'John' => [ + 'name' => 'John', + 'groups' => ['Group2'] + ], + 'Eve' => [ + 'name' => 'Eve', + 'groups' => ['Group2'] + ] + ]; + + $return = $this->externalApiController->sharedRoster($user->getUID()); + + $this->assertEquals('success', $return['result']); + + foreach($return['data']['sharedRoster'] as $key=>$value) { + $this->assertEquals($expectedResult[$key], $value); + } + } + + public function testSharedRosterMultipleOverlapGroups() { + $user = $this->createUserMock('foobar'); + + $this->userManager + ->expects($this->once()) + ->method('get') + ->with($user->getUID()) + ->willReturn($user); + + $this->groupManager + ->expects($this->once()) + ->method('getUserGroups') + ->with($user) + ->willReturn([ + $this->createGroupMock('Group1', ['Foo Bar', 'John Doo', 'user42']), + $this->createGroupMock('Group2', ['user42', 'John', 'Foo Bar']) + ]); + + $expectedResult = [ + 'Foo-Bar' => [ + 'name' => 'Foo Bar', + 'groups' => ['Group1', 'Group2'] + ], + 'John-Doo' => [ + 'name' => 'John Doo', + 'groups' => ['Group1'] + ], + 'user42' => [ + 'name' => 'user42', + 'groups' => ['Group1', 'Group2'] + ], + 'John' => [ + 'name' => 'John', + 'groups' => ['Group2'] + ] + ]; + + $return = $this->externalApiController->sharedRoster($user->getUID()); + + $this->assertEquals('success', $return['result']); + + foreach($return['data']['sharedRoster'] as $key=>$value) { + $this->assertEquals($expectedResult[$key], $value); + } + } + + private function createGroupMock($groupName, $displayNames = []) { + $users = []; + + foreach($displayNames as $displayName) { + $users[] = $this->createUserMock($displayName); + } + + $group = $this->createMock(IGroup::class); + + $group + ->method('getDisplayName') + ->willReturn($groupName); + + $group + ->method('getUsers') + ->willReturn($users); + + return $group; + } + + private function createUserMock($displayName) { + $user = $this->createMock(IUser::class); + + $user + ->method('getUID') + ->willReturn(preg_replace('/ /', '-', $displayName)); + + $user + ->method('getDisplayName') + ->willReturn($displayName); + + return $user; + } +} From 239f73e047bd0e8b7d63f3b532b3e84efed408d8 Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 13:06:13 +0200 Subject: [PATCH 25/35] add tests for settingsController --- appinfo/application.php | 3 +- lib/Controller/SettingsController.php | 91 +++++---- lib/TimeLimitedToken.php | 38 ++++ .../controller/SettingsControllerTest.php | 191 ++++++++++++++++++ 4 files changed, 278 insertions(+), 45 deletions(-) create mode 100644 lib/TimeLimitedToken.php create mode 100644 tests/unit/controller/SettingsControllerTest.php diff --git a/appinfo/application.php b/appinfo/application.php index 11b136a4..14a05f1a 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -73,7 +73,8 @@ public function __construct(array $urlParams=array()){ $c->query('AppName'), $c->query('Request'), $c->query('Config'), - $c->query('UserManager') + $c->query('UserManager'), + \OC::$server->getUserSession() ); }); diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 910e2b7e..28a1800c 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -6,6 +6,8 @@ use OCP\IConfig; use OCP\IRequest; use OCP\IUserManager; +use OCP\IUserSession; +use OCA\OJSXC\TimeLimitedToken; class SettingsController extends Controller { @@ -18,14 +20,14 @@ class SettingsController extends Controller public function __construct($appName, IRequest $request, IConfig $config, - IUserManager $userManager) + IUserManager $userManager, + IUserSession $userSession) { parent::__construct($appName, $request); $this->config = $config; $this->userManager = $userManager; - - $this->userSession = \OC::$server->getUserSession(); + $this->userSession = $userSession; } /** @@ -96,7 +98,7 @@ public function index() $data['xmpp']['username'] = $currentUID; } - $this->generateTimeLimitedToken($data['xmpp']['username'], $data['xmpp']['domain']); + $token = $this->generateTimeLimitedToken($data['xmpp']['username'], $data['xmpp']['domain']); $data['xmpp']['password'] = $token; } @@ -121,27 +123,27 @@ public function setAdmin() { ); } - $this->setAppValue('serverType', $_POST ['serverType']); - $this->setAppValue('boshUrl', trim($_POST ['boshUrl'])); - $this->setAppValue('xmppDomain', trim($_POST ['xmppDomain'])); - $this->setAppValue('xmppResource', trim($_POST ['xmppResource'])); - $this->setAppValue('xmppOverwrite', $this->getCheckboxValue($_POST ['xmppOverwrite'])); - $this->setAppValue('xmppStartMinimized', $this->getCheckboxValue($_POST ['xmppStartMinimized'])); - $this->setAppValue('xmppPreferMail', $this->getCheckboxValue($_POST ['xmppPreferMail'])); + $this->setAppValue('serverType', $this->getParam('serverType')); + $this->setAppValue('boshUrl', $this->getTrimParam('boshUrl')); + $this->setAppValue('xmppDomain', $this->getTrimParam('xmppDomain')); + $this->setAppValue('xmppResource', $this->getTrimParam('xmppResource')); + $this->setAppValue('xmppOverwrite', $this->getCheckboxParam('xmppOverwrite')); + $this->setAppValue('xmppStartMinimized', $this->getCheckboxParam('xmppStartMinimized')); + $this->setAppValue('xmppPreferMail', $this->getCheckboxParam('xmppPreferMail')); - $this->setAppValue('iceUrl', trim($_POST ['iceUrl'])); - $this->setAppValue('iceUsername', trim($_POST ['iceUsername'])); - $this->setAppValue('iceCredential', $_POST ['iceCredential']); - $this->setAppValue('iceSecret', $_POST ['iceSecret']); - $this->setAppValue('iceTtl', $_POST ['iceTtl']); + $this->setAppValue('iceUrl', $this->getTrimParam('iceUrl')); + $this->setAppValue('iceUsername', $this->getTrimParam('iceUsername')); + $this->setAppValue('iceCredential', $this->getParam('iceCredential')); + $this->setAppValue('iceSecret', $this->getParam('iceSecret')); + $this->setAppValue('iceTtl', $this->getParam('iceTtl')); - $this->setAppValue('timeLimitedToken', $this->getCheckboxValue($_POST ['timeLimitedToken'])); + $this->setAppValue('timeLimitedToken', $this->getCheckboxParam('timeLimitedToken')); - $this->setAppValue('firefoxExtension', $_POST ['firefoxExtension']); - $this->setAppValue('chromeExtension', $_POST ['chromeExtension']); + $this->setAppValue('firefoxExtension', $this->getParam('firefoxExtension')); + $this->setAppValue('chromeExtension', $this->getParam('chromeExtension')); $externalServices = array(); - foreach($_POST['externalServices'] as $es) { + foreach($this->getParam('externalServices') as $es) { if (preg_match('/^(https:\/\/)?([\w\d*][\w\d-]*)(\.[\w\d-]+)+(:[\d]+)?$/', $es)) { $externalServices[] = $es; } @@ -178,19 +180,21 @@ public function setUser() { */ public function getIceServers() { $secret = $this->getAppValue('iceSecret'); - $uid = $this->userSession->getUser()->getUID(); - $ttl = $this->getAppValue('iceTtl', 3600 * 24); // one day (according to TURN-REST-API) $url = $this->getAppValue('iceUrl'); + $username = $this->getAppValue('iceUsername', ''); + $credential = $this->getAppValue('iceCredential', ''); + $url = preg_match('/^(turn|stun):/', $url) || empty($url) ? $url : "turn:$url"; - $usernameTRA = $secret ? (time() + $ttl).':'.$uid : $uid; - $username = $this->getAppValue('iceUsername', ''); - $username = (!empty($username)) ? $username : $usernameTRA; + if (!empty($secret) && (empty($username) || empty($credential))) { + $uid = $this->userSession->getUser()->getUID(); - $credentialTRA = ($secret) ? base64_encode(hash_hmac('sha1', $username, $secret, true)) : ''; - $credential = $this->getAppValue('iceCredential', ''); - $credential = (!empty($credential)) ? $credential : $credentialTRA; + $accessData = TimeLimitedToken::generateTURN($uid, $secret, $ttl); + + $username = $accessData[0]; + $credential = $accessData[1]; + } if (!empty($url)) { $data = array( @@ -246,8 +250,8 @@ private function getCurrentUser() if (\OCP\User::isLoggedIn()) { $currentUser = $this->userSession->getUser(); - } elseif (!empty($_POST['password']) && !empty($_POST['username'])) { - $currentUser = $this->userManager->checkPassword($_POST['username'], $_POST['password']); + } elseif (!empty($this->getParam('username')) && !empty($this->getParam('password'))) { + $currentUser = $this->userManager->checkPassword($this->getParam('username'), $this->getParam('password')); } return $currentUser; @@ -255,22 +259,9 @@ private function getCurrentUser() private function generateTimeLimitedToken($node, $domain) { - $jid = $node. '@' . $domain; - $expiry = time() + 60*60; $secret = $this->getAppValue('apiSecret'); - $version = hex2bin('00'); - $secretID = substr(hash('sha256', $secret, true), 0, 2); - $header = $secretID.pack('N', $expiry); - $challenge = $version.$header.$jid; - $hmac = hash_hmac('sha256', $challenge, $secret, true); - $token = $version.substr($hmac, 0, 16).$header; - - // format as "user-friendly" base64 - $token = str_replace('=', '', strtr(base64_encode($token), - 'OIl', '-$%')); - - return $token; + return TimeLimitedToken::generateUser($node, $domain, $secret); } private function overwriteByUserDefined($currentUID, $data) @@ -331,4 +322,16 @@ private function isPasswordConfirmationRequired() { private function getCheckboxValue($var) { return (isset($var)) ? $var : 'false'; } + + private function getParam($key) { + return $this->request->getParam($key); + } + + private function getCheckboxParam($key) { + return $this->getCheckboxValue($this->request->getParam($key)); + } + + private function getTrimParam($key) { + return trim($this->request->getParam($key)); + } } diff --git a/lib/TimeLimitedToken.php b/lib/TimeLimitedToken.php new file mode 100644 index 00000000..54838e14 --- /dev/null +++ b/lib/TimeLimitedToken.php @@ -0,0 +1,38 @@ +request = $this->createMock(IRequest::class); + $this->config = $this->createMock(IConfig::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->userSession = $this->createMock(IUserSession::class); + + $this->settingsController = new SettingsController( + 'ojsxc', + $this->request, + $this->config, + $this->userManager, + $this->userSession + ); + } + + public function testIndexWithoutUser() { + $return = $this->settingsController->index(); + + $this->assertEquals('noauth', $return['result']); + } + + public function testIndexDefaultServerType() { + $this->expectsInternalServerSettings(null); + } + + public function testIndexServerTypeInternal() { + $this->expectsInternalServerSettings('internal'); + } + + public function testIndexPreferPersonalEmail() { + $mapGetAppValue = [ + ['ojsxc', 'serverType', null, 'external'], + ['ojsxc', 'xmppPreferMail', null, 'true'] + ]; + + $node = 'foobar'; + $domain = 'host'; + + $mapGetUserValue = [ + ['Foo', 'settings', 'email', '', $node.'@'.$domain] + ]; + + $this->setUpAuthenticatedIndex($mapGetAppValue, $mapGetUserValue); + + $return = $this->settingsController->index(); + + $this->assertEquals('success', $return['result']); + $this->assertEquals($node, $return['data']['xmpp']['username']); + $this->assertEquals($domain, $return['data']['xmpp']['domain']); + } + + public function testIndexTimeLimitedToken() { + $mapGetAppValue = [ + ['ojsxc', 'serverType', null, 'external'], + ['ojsxc', 'timeLimitedToken', null, 'true'], + ['ojsxc', 'xmppDomain', null, 'localhost'] + ]; + + $this->setUpAuthenticatedIndex($mapGetAppValue); + + $return = $this->settingsController->index(); + + $this->assertEquals('success', $return['result']); + $this->assertNotEquals(null, $return['data']['xmpp']['password']); + } + + public function testGetIceServersNoData() { + $this->setUpGetIceServers(); + + $return = $this->settingsController->getIceServers(); + + $this->assertEquals([], $return); + } + + public function testGetIceServersStoredDataWithPrefix() { + $this->setUpGetIceServers('turn:localhost', '12345', 'foobar', 'password', 'secret'); + + $return = $this->settingsController->getIceServers(); + + $this->assertEquals('12345', $return['ttl']); + $this->assertEquals('turn:localhost', $return['iceServers'][0]['urls'][0]); + $this->assertEquals('foobar', $return['iceServers'][0]['username']); + $this->assertEquals('password', $return['iceServers'][0]['credential']); + } + + public function testGetIceServersGeneratedToken() { + $ttl = 12345; + $this->setUpGetIceServers('turn:localhost', ''.$ttl, '', 'password', 'secret'); + + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($this->createUserMock('Foo')); + + $return = $this->settingsController->getIceServers(); + + $this->assertEquals('12345', $return['ttl']); + $this->assertEquals('turn:localhost', $return['iceServers'][0]['urls'][0]); + + $username = $return['iceServers'][0]['username']; + list($validUntil, $uid) = explode(':', $username); + + $this->assertGreaterThan(time(), intval($validUntil)); + $this->assertLessThanOrEqual(time() + $ttl, intval($validUntil)); + $this->assertEquals('Foo', $uid); + $this->assertNotEquals('password', $return['iceServers'][0]['credential']); + $this->assertFalse(empty($return['iceServers'][0]['credential'])); + } + + private function expectsInternalServerSettings($serverType) { + $mapGetAppValue = [ + ['ojsxc', 'serverType', null, $serverType] + ]; + + $this->setUpAuthenticatedIndex($mapGetAppValue); + + $this->request + ->expects($this->once()) + ->method('getServerHost') + ->willReturn('localhost'); + + $return = $this->settingsController->index(); + + $this->assertEquals('success', $return['result']); + $this->assertEquals('internal', $return['data']['serverType']); + $this->assertEquals('localhost', $return['data']['adminSettings']['xmppDomain']); + } + + private function setUpAuthenticatedIndex($mapGetAppValue = [], $mapGetUserValue = []) { + $mapGetParam = [ + ['username', null, 'foo'], + ['password', null, 'bar'] + ]; + + $this->request->method('getParam')->will($this->returnValueMap($mapGetParam)); + $this->config->method('getAppValue')->will($this->returnValueMap($mapGetAppValue)); + $this->config->method('getUserValue')->will($this->returnValueMap($mapGetUserValue)); + + $this->userManager + ->expects($this->once()) + ->method('checkPassword') + ->with('foo', 'bar') + ->willReturn($this->createUserMock('Foo')); + } + + private function setUpGetIceServers($iceUrl = '', $iceTtl = '', $iceUsername = '', $iceCredential = '', $iceSecret = '') { + $mapGetAppValue = [ + ['ojsxc', 'iceSecret', null, $iceSecret], + ['ojsxc', 'iceTtl', 3600*24, $iceTtl], + ['ojsxc', 'iceUrl', null, $iceUrl], + ['ojsxc', 'iceUsername', '', $iceUsername], + ['ojsxc', 'iceCredential', '', $iceCredential] + ]; + + $this->config->method('getAppValue')->will($this->returnValueMap($mapGetAppValue)); + } + + private function createUserMock($displayName) { + $user = $this->createMock(IUser::class); + + $user + ->method('getUID') + ->willReturn(preg_replace('/ /', '-', $displayName)); + + $user + ->method('getDisplayName') + ->willReturn($displayName); + + return $user; + } +} From 7868fb697fbde042f46820be0ebf093f4ae0eca4 Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 13:18:46 +0200 Subject: [PATCH 26/35] add tests for TimeLimitedToken --- tests/unit/TimeLimitedTokenTest.php | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/unit/TimeLimitedTokenTest.php diff --git a/tests/unit/TimeLimitedTokenTest.php b/tests/unit/TimeLimitedTokenTest.php new file mode 100644 index 00000000..f7ab7896 --- /dev/null +++ b/tests/unit/TimeLimitedTokenTest.php @@ -0,0 +1,46 @@ +assertEquals( + 'AJP4Mvv5P8qZZJcENQhzfH$ruF%1458', + TimeLimitedToken::generateUser('foo', 'bar', 'secret', 60*60, 1500894607) + ); + + $this->assertEquals( + 'AELcjDTQjxEJptXaWb29gkt+LF%2Yi8', + TimeLimitedToken::generateUser('foo-bar', 'localhost.xyz', 'AJP4Mvv5P8', 60*60*10, 1500894607) + ); + + $this->assertEquals( + 'AEU+Upmh-jRtoHQ2Um1cYMcMV1%2Yi8', + TimeLimitedToken::generateUser('foo.bar', 'local.host.xyz', 'iiGTp+LF%2', 60*60*10, 1500894607) + ); + } + + public function testGenerateTURN() { + $this->assertEquals( + [(60*60 + 1500894607).':foobar', 'u66TdvZP9USnoCeOBFtVQa4DCkw='], + TimeLimitedToken::generateTURN('foobar', 'secret', 60*60, 1500894607) + ); + + $this->assertEquals( + [(3600 * 24 + 1500894607).':foo.bar', 'zfLkyJlJPx+KnLo5eLEUwJXDbGo='], + TimeLimitedToken::generateTURN('foo.bar', 'CeOBFtVQa', 3600 * 24, 1500894607) + ); + + $this->assertEquals( + [(3600 * 24 + 1500894607).':foo:bar', 'e+dKdn0JtGWccYCJ3NKaDUD6JZk='], + TimeLimitedToken::generateTURN('foo:bar', 'nLo5eLEUwJXD', 3600 * 24, 1500894607) + ); + + $this->assertEquals( + [(3600 * 24 + 1500894607).':foobar', 'q01XUfO0p37h5dGDd5R2PO2RhpM='], + TimeLimitedToken::generateTURN('foobar', 'nLo5eLEUwJXD', 3600 * 24, 1500894607) + ); + } +} From 5622dec77ae01679717ca9d320e4c519b99d959f Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 13:20:47 +0200 Subject: [PATCH 27/35] change external api url - rename externalApi to api - add version --- appinfo/routes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index f2e3f9d5..89d20772 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -20,9 +20,9 @@ array('name' => 'settings#getIceServers', 'url' => '/settings/iceServers', 'verb' => 'GET'), array('name' => 'settings#getUsers', 'url' => '/settings/users', 'verb' => 'GET'), array('name' => 'externalApi#index', 'url' => '/ajax/externalApi.php', 'verb' => 'POST'), - array('name' => 'externalApi#checkPassword', 'url' => '/externalApi/checkPassword', 'verb' => 'POST'), - array('name' => 'externalApi#isUser', 'url' => '/externalApi/isUser', 'verb' => 'POST'), - array('name' => 'externalApi#sharedRoster', 'url' => '/externalApi/sharedRoster', 'verb' => 'POST'), + array('name' => 'externalApi#check_password', 'url' => '/api/v2/checkPassword', 'verb' => 'POST'), + array('name' => 'externalApi#is_user', 'url' => '/api/v2/isUser', 'verb' => 'POST'), + array('name' => 'externalApi#shared_roster', 'url' => '/api/v2/sharedRoster', 'verb' => 'POST'), ) )); ?> From a8f0ce11bfba6bb8f456f922f9d11d946e57688f Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 13:22:08 +0200 Subject: [PATCH 28/35] remove license header --- appinfo/app.php | 7 ------- appinfo/routes.php | 8 -------- 2 files changed, 15 deletions(-) diff --git a/appinfo/app.php b/appinfo/app.php index 9baf7848..29c6d6fb 100755 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -1,12 +1,5 @@
- * Released under the MIT license - * @author Klaus Herberth - */ use OCA\OJSXC\AppInfo\Application; if (!interface_exists('\OCP\Settings\ISettings')) { diff --git a/appinfo/routes.php b/appinfo/routes.php index 89d20772..bbb441ac 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -1,12 +1,4 @@
- * Released under the MIT license - * - * @author Klaus Herberth - */ use \OCA\OJSXC\AppInfo\Application; From 84d05568f95969857f6965e29aff836edf339189 Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 13:44:01 +0200 Subject: [PATCH 29/35] allow authentication with app password (from #38) --- appinfo/application.php | 1 + lib/Controller/ExternalApiController.php | 19 +++++++------ .../controller/ExternalApiControllerTest.php | 28 +++++++++++++------ 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/appinfo/application.php b/appinfo/application.php index 14a05f1a..ef3457e3 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -83,6 +83,7 @@ public function __construct(array $urlParams=array()){ $c->query('AppName'), $c->query('Request'), $c->query('UserManager'), + \OC::$server->getUserSession(), $c->query('GroupManager'), $c->query('Logger') ); diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php index 00be017b..952b1d47 100644 --- a/lib/Controller/ExternalApiController.php +++ b/lib/Controller/ExternalApiController.php @@ -5,6 +5,7 @@ use OCP\IConfig; use OCP\IRequest; use OCP\IUserManager; +use OCP\IUserSession; use OCP\IGroupManager; use OCP\ILogger; use OCP\AppFramework\Http\JSONResponse; @@ -14,25 +15,24 @@ class ExternalApiController extends SignatureProtectedApiController { private $userManager; - private $groupManager; - private $userSession; + private $groupManager; + private $logger; public function __construct($appName, IRequest $request, IUserManager $userManager, + IUserSession $userSession, IGroupManager $groupManager, ILogger $logger) { parent::__construct($appName, $request); $this->userManager = $userManager; + $this->userSession = $userSession; $this->groupManager = $groupManager; - - $this->userSession = \OC::$server->getUserSession(); - $this->logger = $logger; } @@ -70,10 +70,13 @@ public function checkPassword($username = '', $password = '', $domain = '') { if(!empty($password) && !empty($username)) { if(!empty($domain)) { - $currentUser = $this->userManager->checkPassword($username . '@' . $domain, $password); + $loggedIn = $this->userSession->login($username . '@' . $domain, $password); + } else { + $loggedIn = $this->userSession->login($username, $password); } - if($currentUser === null) { - $currentUser = $this->userManager->checkPassword($username, $password); + + if($loggedIn === true) { + $currentUser = $this->userSession->getUser(); } } diff --git a/tests/unit/controller/ExternalApiControllerTest.php b/tests/unit/controller/ExternalApiControllerTest.php index 61d2a5b2..318930c8 100644 --- a/tests/unit/controller/ExternalApiControllerTest.php +++ b/tests/unit/controller/ExternalApiControllerTest.php @@ -6,6 +6,7 @@ use OCA\OJSXC\Controller\SignatureProtectedApiController; use OCP\IRequest; use OCP\IUserManager; +use OCP\IUserSession; use OCP\IGroupManager; use OCP\ILogger; use OCP\IUser; @@ -15,6 +16,7 @@ class ExternalApiControllerTest extends TestCase { private $request; private $userManager; + private $userSession; private $groupManager; private $logger; private $user; @@ -25,6 +27,7 @@ public function setUp() { $this->request = $this->createMock(IRequest::class); $this->userManager = $this->createMock(IUserManager::class); + $this->userSession = $this->createMock(IUserSession::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->logger = $this->createMock(ILogger::class); $this->user = $this->createMock(IUser::class); @@ -33,6 +36,7 @@ public function setUp() { 'ojsxc', $this->request, $this->userManager, + $this->userSession, $this->groupManager, $this->logger ); @@ -43,9 +47,9 @@ public function testSignatureProtected() { } public function testCheckPasswordWithInvalidParams() { - $this->userManager + $this->userSession ->expects($this->once()) - ->method('checkPassword') + ->method('login') ->with('foo', 'bar') ->willReturn(false); @@ -55,9 +59,9 @@ public function testCheckPasswordWithInvalidParams() { } public function testCheckPasswordWithInvalidParamsAndDomain() { - $this->userManager + $this->userSession ->expects($this->once()) - ->method('checkPassword') + ->method('login') ->with('foo@localhost', 'bar') ->willReturn(false); @@ -72,10 +76,14 @@ public function testCheckPasswordWithValidParams() { ->expects($this->once()) ->method('getUID') ->willReturn($uid); - $this->userManager + $this->userSession ->expects($this->once()) - ->method('checkPassword') + ->method('login') ->with('foo', 'bar') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') ->willReturn($this->user); $return = $this->externalApiController->checkPassword('foo', 'bar'); @@ -90,10 +98,14 @@ public function testCheckPasswordWithValidParamsAndDomain() { ->expects($this->once()) ->method('getUID') ->willReturn($uid); - $this->userManager + $this->userSession ->expects($this->once()) - ->method('checkPassword') + ->method('login') ->with('foo@localhost', 'bar') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') ->willReturn($this->user); $return = $this->externalApiController->checkPassword('foo', 'bar', 'localhost'); From 277ca51b823d2892f5106d8992f0ed9132ac071f Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 13:59:27 +0200 Subject: [PATCH 30/35] filter exceptions in middleware --- lib/Controller/ExternalApiController.php | 5 +++-- lib/Exceptions/Exception.php | 5 +++++ lib/Exceptions/SecurityException.php | 2 +- lib/Exceptions/UnprocessableException.php | 5 +++++ lib/Middleware/ExternalApiMiddleware.php | 18 +++++++++++------- 5 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 lib/Exceptions/Exception.php create mode 100644 lib/Exceptions/UnprocessableException.php diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php index 952b1d47..0c413880 100644 --- a/lib/Controller/ExternalApiController.php +++ b/lib/Controller/ExternalApiController.php @@ -10,6 +10,7 @@ use OCP\ILogger; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http; +use OCA\OJSXC\Exceptions\UnprocessableException; class ExternalApiController extends SignatureProtectedApiController { @@ -59,7 +60,7 @@ public function index($operation) ); break; default: - throw new \Exception( 'Unsupported operation.'); + throw new UnprocessableException( 'Unsupported operation.'); } } @@ -122,7 +123,7 @@ public function sharedRoster($username = '', $domain = '') { $username .= '@' . $domain; } } else { - throw new \Exception('No username provided'); + throw new UnprocessableException('No username provided'); } $roster = []; diff --git a/lib/Exceptions/Exception.php b/lib/Exceptions/Exception.php new file mode 100644 index 00000000..950f4c7a --- /dev/null +++ b/lib/Exceptions/Exception.php @@ -0,0 +1,5 @@ + 'error', - 'data' => array( - 'msg' => $exception->getMessage() - ) - ), Http::STATUS_INTERNAL_SERVER_ERROR); + if($exception instanceof Exception) { + return new JSONResponse(array( + 'result' => 'error', + 'data' => array( + 'msg' => $exception->getMessage() + ) + ), Http::STATUS_INTERNAL_SERVER_ERROR); + } + + throw $exception; } } From 1725f83c96e4e4b28057eb34109a90b4a1a0fdab Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 14:21:37 +0200 Subject: [PATCH 31/35] test isUser and checkPassword with and without domain --- lib/Controller/ExternalApiController.php | 6 ++++-- .../controller/ExternalApiControllerTest.php | 16 ++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php index 0c413880..b56e00a5 100644 --- a/lib/Controller/ExternalApiController.php +++ b/lib/Controller/ExternalApiController.php @@ -72,7 +72,8 @@ public function checkPassword($username = '', $password = '', $domain = '') { if(!empty($password) && !empty($username)) { if(!empty($domain)) { $loggedIn = $this->userSession->login($username . '@' . $domain, $password); - } else { + } + if(!$loggedIn) { $loggedIn = $this->userSession->login($username, $password); } @@ -104,7 +105,8 @@ public function isUser($username = '', $domain = '') { if(!empty($username)) { if(!empty($domain)) { $isUser = $this->userManager->userExists($username . '@' . $domain); - } else { + } + if(!$isUser) { $isUser = $this->userManager->userExists($username); } } diff --git a/tests/unit/controller/ExternalApiControllerTest.php b/tests/unit/controller/ExternalApiControllerTest.php index 318930c8..7f5c44d9 100644 --- a/tests/unit/controller/ExternalApiControllerTest.php +++ b/tests/unit/controller/ExternalApiControllerTest.php @@ -60,10 +60,12 @@ public function testCheckPasswordWithInvalidParams() { public function testCheckPasswordWithInvalidParamsAndDomain() { $this->userSession - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('login') - ->with('foo@localhost', 'bar') - ->willReturn(false); + ->will($this->returnValueMap([ + ['foo@localhost', 'bar', false], + ['foo', 'bar', false] + ])); $return = $this->externalApiController->checkPassword('foo', 'bar', 'localhost'); @@ -129,10 +131,12 @@ public function testIsUserFail() { public function testIsUserFailWithDomain() { $this->userManager - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('userExists') - ->with('foo@localhost') - ->willReturn(false); + ->will($this->returnValueMap([ + ['foo@localhost', false], + ['foo', false] + ])); $return = $this->externalApiController->isUser('foo', 'localhost'); From 514ecb7977d363086a98029caed37451132ddeb4 Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 17:42:18 +0200 Subject: [PATCH 32/35] remove deprecated settings/admin.php --- appinfo/app.php | 4 ---- settings/admin.php | 34 ---------------------------------- 2 files changed, 38 deletions(-) delete mode 100644 settings/admin.php diff --git a/appinfo/app.php b/appinfo/app.php index 29c6d6fb..a3630d29 100755 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -2,10 +2,6 @@ use OCA\OJSXC\AppInfo\Application; -if (!interface_exists('\OCP\Settings\ISettings')) { - \OCP\App::registerAdmin ( 'ojsxc', 'settings/admin' ); -} - \OCP\App::registerPersonal('ojsxc', 'settings/personal'); $jsxc_root = (defined('JSXC_ENV') && JSXC_ENV === 'dev')? 'jsxc/dev/' : 'jsxc/'; diff --git a/settings/admin.php b/settings/admin.php deleted file mode 100644 index dc50ff29..00000000 --- a/settings/admin.php +++ /dev/null @@ -1,34 +0,0 @@ -getConfig(); -$tmpl = new OCP\Template('ojsxc', 'settings/admin'); - -$serverType = $config->getAppValue('ojsxc', 'serverType'); - -$tmpl->assign('serverType', (!empty($serverType))? $serverType : 'internal'); -$tmpl->assign('boshUrl', $config->getAppValue('ojsxc', 'boshUrl')); -$tmpl->assign('xmppDomain', $config->getAppValue('ojsxc', 'xmppDomain')); -$tmpl->assign('xmppPreferMail', $config->getAppValue('ojsxc', 'xmppPreferMail')); -$tmpl->assign('xmppResource', $config->getAppValue('ojsxc', 'xmppResource')); -$tmpl->assign('xmppOverwrite', $config->getAppValue('ojsxc', 'xmppOverwrite')); -$tmpl->assign('xmppStartMinimized', $config->getAppValue('ojsxc', 'xmppStartMinimized')); -$tmpl->assign('iceUrl', $config->getAppValue('ojsxc', 'iceUrl')); -$tmpl->assign('iceUsername', $config->getAppValue('ojsxc', 'iceUsername')); -$tmpl->assign('iceCredential', $config->getAppValue('ojsxc', 'iceCredential')); -$tmpl->assign('iceSecret', $config->getAppValue('ojsxc', 'iceSecret')); -$tmpl->assign('iceTtl', $config->getAppValue('ojsxc', 'iceTtl')); -$tmpl->assign('firefoxExtension', $config->getAppValue('ojsxc', 'firefoxExtension')); -$tmpl->assign('chromeExtension', $config->getAppValue('ojsxc', 'chromeExtension')); - -$externalServices = $config->getAppValue('ojsxc', 'externalServices'); -$externalServices = explode("|", $externalServices); -$tmpl->assign('externalServices', $externalServices); - -$tmpl->assign('apiSecret', $config->getAppValue('ojsxc', 'apiSecret')); -$tmpl->assign('timeLimitedToken', $config->getAppValue('ojsxc', 'timeLimitedToken')); - -return $tmpl->fetchPage(); From 0155e7dbc7c58bb6ec00ee22fbdac11a4d2b18d3 Mon Sep 17 00:00:00 2001 From: sualko Date: Mon, 24 Jul 2017 17:54:48 +0200 Subject: [PATCH 33/35] use public method instead of private :tada: app is compliant --- settings/personal.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/personal.php b/settings/personal.php index bf77748b..63da6d80 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -1,6 +1,6 @@ Date: Mon, 24 Jul 2017 17:55:27 +0200 Subject: [PATCH 34/35] update npm dependencies --- package.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 569cc0ed..5d3175a7 100644 --- a/package.json +++ b/package.json @@ -11,20 +11,20 @@ "url": "https://github.com/jsxc/jsxc.nextcloud" }, "devDependencies": { - "grunt": "~0.4.4", - "grunt-autoprefixer": "^2.2.0", - "grunt-banner": "~0.2.2", - "grunt-cli": "~0.1.13", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-compress": "^0.10.0", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-jshint": "~0.9.2", - "grunt-contrib-watch": "^0.6.1", - "grunt-data-uri": "^0.2.0", - "grunt-exec": "^1.0.1", + "grunt": "~1.0.1", + "grunt-autoprefixer": "^3.0.4", + "grunt-banner": "~0.6.0", + "grunt-cli": "~1.2.0", + "grunt-contrib-clean": "~1.1.0", + "grunt-contrib-compress": "^1.4.3", + "grunt-contrib-copy": "~1.0.0", + "grunt-contrib-jshint": "~1.1.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-data-uri": "^0.3.0", + "grunt-exec": "^2.0.0", "grunt-sass": "2.0.0", "grunt-search": "^0.1.6", - "grunt-text-replace": "~0.3.11", - "node-sass": "4.5.2" + "grunt-text-replace": "~0.4.0", + "node-sass": "4.5.3" } } From 21b9671a4d58e7126cd05dc793b84cf1956b78df Mon Sep 17 00:00:00 2001 From: sualko Date: Tue, 25 Jul 2017 10:15:37 +0200 Subject: [PATCH 35/35] fix multiple php warnings --- lib/Controller/ExternalApiController.php | 3 ++- lib/Controller/SettingsController.php | 2 +- lib/TimeLimitedToken.php | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Controller/ExternalApiController.php b/lib/Controller/ExternalApiController.php index b56e00a5..36e0f60e 100644 --- a/lib/Controller/ExternalApiController.php +++ b/lib/Controller/ExternalApiController.php @@ -70,6 +70,7 @@ public function checkPassword($username = '', $password = '', $domain = '') { $this->logger->info('ExAPI: Check password for user: '.$username.'@'.$domain); if(!empty($password) && !empty($username)) { + $loggedIn = false; if(!empty($domain)) { $loggedIn = $this->userSession->login($username . '@' . $domain, $password); } @@ -137,7 +138,7 @@ public function sharedRoster($username = '', $domain = '') { foreach($userGroup->getUsers() as $user) { $uid = $user->getUID(); - if(!$roster[$uid]) { + if(!array_key_exists($uid, $roster)) { $roster[$uid] = [ 'name' => $user->getDisplayName(), 'groups' => [] diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 28a1800c..68be1ef3 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -94,7 +94,7 @@ public function index() } if ($this->getBooleanAppValue('timeLimitedToken')) { - if (!is_string($data['xmpp']['username'])) { + if (!array_key_exists('username', $data['xmpp']) || empty($data['xmpp']['username'])) { $data['xmpp']['username'] = $currentUID; } diff --git a/lib/TimeLimitedToken.php b/lib/TimeLimitedToken.php index 54838e14..17bcefc8 100644 --- a/lib/TimeLimitedToken.php +++ b/lib/TimeLimitedToken.php @@ -3,8 +3,8 @@ namespace OCA\OJSXC; class TimeLimitedToken { - public static function generateUser($node, $domain, $secret, $ttl = 60*60, $time) { - if (!isset($time)) { + public static function generateUser($node, $domain, $secret, $ttl = 60*60, $time = null) { + if (!isset($time) || $time === null) { $time = time(); } @@ -25,8 +25,8 @@ public static function generateUser($node, $domain, $secret, $ttl = 60*60, $time return $token; } - public static function generateTURN($uid, $secret, $ttl = 3600 * 24, $time) { - if (!isset($time)) { + public static function generateTURN($uid, $secret, $ttl = 3600 * 24, $time = null) { + if (!isset($time) || $time === null) { $time = time(); }