From f317df3c121003414f2d64c2ce34089bcdca9355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 23 Sep 2015 15:03:47 +0200 Subject: [PATCH] add configuration UI --- README.md | 2 +- appinfo/app.php | 6 +- appinfo/routes.php | 25 ++++++ controller/settingscontroller.php | 82 ++++++++++++++++++ js/guests.js | 136 ++++++++++++++++++++++++++++++ lib/backend.php | 45 ++++++++-- lib/hooks.php | 10 ++- settings/admin.php | 15 ++++ templates/settings/admin.php | 28 ++++++ 9 files changed, 340 insertions(+), 9 deletions(-) create mode 100644 appinfo/routes.php create mode 100644 controller/settingscontroller.php create mode 100644 js/guests.js create mode 100644 settings/admin.php create mode 100644 templates/settings/admin.php diff --git a/README.md b/README.md index 0a43f45..f7de4eb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Allow classifying users or contacts as guests Jails a user into a readonly home storage -* TODO If his quota is 0 +* TODO If his quota is 0 TODO below x * TODO If he is member of a configurable group * If he is a contact of another user and has been shared a file diff --git a/appinfo/app.php b/appinfo/app.php index 397385e..f109d06 100755 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -10,8 +10,12 @@ */ $guestBackend = \OCA\Guests\Backend::createForStaticLegacyCode(); -\OC_User::useBackend($guestBackend); +\OC::$server->getUserManager()->registerBackend($guestBackend); + + +\OCP\App::registerAdmin('guests', 'settings/admin'); \OCP\Util::connectHook('OC_Filesystem', 'preSetup', '\OCA\Guests\Hooks', 'preSetup'); \OCP\Util::connectHook('OCP\Share', 'pre_shared', '\OCA\Guests\Hooks', 'preShareHook'); \OCP\Util::connectHook('OCP\Share', 'post_shared', '\OCA\Guests\Hooks', 'postShareHook'); + diff --git a/appinfo/routes.php b/appinfo/routes.php new file mode 100644 index 0000000..f1af1f2 --- /dev/null +++ b/appinfo/routes.php @@ -0,0 +1,25 @@ + + * @copyright Jörn Friedrich Dreyer 2015 + */ + +return [ + 'routes' => [ + [ + 'name' => 'settings#getConfig', + 'url' => '/config', + 'verb' => 'GET', + ], + [ + 'name' => 'settings#setConfig', + 'url' => '/config', + 'verb' => 'PUT', + ], + ], +]; \ No newline at end of file diff --git a/controller/settingscontroller.php b/controller/settingscontroller.php new file mode 100644 index 0000000..d87b864 --- /dev/null +++ b/controller/settingscontroller.php @@ -0,0 +1,82 @@ + + * @copyright Jörn Friedrich Dreyer 2015 + */ + +namespace OCA\Guests\Controller; + +use OCA\Guests\Backend; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\IRequest; + +/** + * Class SettingsController is used to handle configuration changes on the + * settings page + * + * @package OCA\Guests\Controller + */ +class SettingsController extends Controller { + + /** + * @var string + */ + private $userId; + + /** + * @var IConfig + */ + private $config; + + public function __construct($AppName, IRequest $request, $UserId, IConfig $config) { + parent::__construct($AppName, $request); + $this->userId = $UserId; + $this->config = $config; + } + + /** + * AJAX handler for getting the config + * + * @return DataResponse with the current config + */ + public function getConfig() { + $conditions = $this->config->getUserValue($this->userId, 'guests', 'conditions', 'quota'); + $conditions = explode(',', $conditions); + $apps = $this->config->getUserValue($this->userId, 'guests', 'apps', Backend::DEFAULT_GUEST_GROUPS); + $apps = explode(',', $apps); + return new DataResponse([ + 'conditions' => $conditions, + 'group' => $this->config->getUserValue($this->userId, 'guests', 'group', 'guests'), + 'apps' => $apps, + ]); + } + /** + * AJAX handler for setting the config + * + * @param $conditions string[] + * @param $group string + * @param $apps string[] + * @return DataResponse + */ + public function setConfig($conditions, $group, $apps) { + $conditions = join(',', $conditions); + $newApps = []; + foreach ($apps as $app) { + $newApps[] = trim($app); + } + $newApps = join(',', $newApps); + $this->config->setUserValue($this->userId, 'guests', 'conditions', $conditions); + $this->config->setUserValue($this->userId, 'guests', 'group', $group); + $this->config->setUserValue($this->userId, 'guests', 'apps', $newApps); + return new DataResponse(); + } + +} diff --git a/js/guests.js b/js/guests.js new file mode 100644 index 0000000..52a2082 --- /dev/null +++ b/js/guests.js @@ -0,0 +1,136 @@ +/** + * ownCloud + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING-AGPL file. + * + * @author Jörn Friedrich Dreyer + * @copyright Jörn Friedrich Dreyer 2015 + */ +(function() { + + $(document).ready(function () { + + // variables + var $section = $('#guests'); + var $guestsByQuota = $section.find('#guestsByQuota'); + var $guestsByGroup = $section.find('#guestsByGroup'); + var $guestGroup = $section.find('#guestGroup'); + var $guestsByContact = $section.find('#guestsByContact'); + var $guestApps = $section.find('#guestApps'); + var $msg = $section.find('.msg'); + + var config = { conditions: ['quota'], group: 'guests', + apps:[ + '','core','settings','avatar','files','files_trashbin','files_sharing' + ]}; + + // functions + + var loadConfig = function () { + OC.msg.startAction($msg, t('guests', 'Loading…')); + $.get( + OC.generateUrl('apps/guests/config'), + '', + function (data) { + // update model + config = data; + // update ui + if($.inArray('quota', config.conditions) > -1) { + $guestsByQuota.prop('checked', true); + } else { + $guestsByQuota.prop('checked', false); + } + if($.inArray('group', config.conditions) > -1) { + $guestsByGroup.prop('checked', true); + } else { + $guestsByGroup.prop('checked', false); + } + if($.inArray('contact', config.conditions) > -1) { + $guestsByContact.prop('checked', true); + } else { + $guestsByContact.prop('checked', false); + } + if (config.group) { + $guestGroup.val(config.group); + } else { + $guestGroup.val(''); + } + if ($.isArray(config.apps)) { + $guestApps.val(config.apps.join()); + } else { + $guestApps.val(''); + } + }, + 'json' + ).then(function() { + var data = { status: 'success', data: {message: t('guests', 'Loaded')} }; + OC.msg.finishedAction($msg, data); + }, function(result) { + var data = { status: 'error', data:{message:result.responseJSON.message} }; + OC.msg.finishedAction($msg, data); + }); + }; + + var saveConfig = function () { + OC.msg.startSaving($msg); + $.ajax({ + type: 'PUT', + url: OC.generateUrl('apps/guests/config'), + data: config, + dataType: 'json' + }).success(function() { + var data = { status:'success', data:{message:t('guests', 'Saved')} }; + OC.msg.finishedSaving($msg, data); + }).fail(function(result) { + var data = { status: 'error', data:{message:result.responseJSON.message} }; + OC.msg.finishedSaving($msg, data); + }); + }; + + // load initial config + loadConfig(); + + var updateConditions = function () { + var conditions = []; + if ($guestsByQuota.prop('checked')) { + conditions.push('quota'); + } + if ($guestsByGroup.prop('checked')) { + conditions.push('group'); + } + if ($guestsByContact.prop('checked')) { + conditions.push('contact'); + } + config.conditions = conditions; + }; + + // listen to ui changes + $guestsByQuota.on('change', function () { + updateConditions(); + saveConfig(); + }); + $guestsByGroup.on('change', function () { + updateConditions(); + saveConfig(); + }); + $guestsByContact.on('change', function () { + updateConditions(); + saveConfig(); + }); + $guestGroup.on('change', function () { + config.group = $guestGroup.val(); + saveConfig(); + }); + $guestApps.on('change', function () { + var apps = $guestApps.val().split(','); + config.apps = []; + $.each(apps, function( index, value ) { + config.apps.push(value.trim()); + }); + saveConfig(); + }); + + }); + +})(); \ No newline at end of file diff --git a/lib/backend.php b/lib/backend.php index e485dbe..e19b37c 100644 --- a/lib/backend.php +++ b/lib/backend.php @@ -43,12 +43,16 @@ class Backend implements UserInterface, IUserBackend { /** @var IUserManager */ private $userManager; + /** @var IGroupManager */ + private $groupManager; + public function __construct( IConfig $config, ILogger $logger, GuestMapper $mapper, IHasher $hasher, IManager $contactsManager, + IUserManager $userManager, IGroupManager $groupManager ) { $this->config = $config; @@ -56,6 +60,7 @@ class Backend implements UserInterface, IUserBackend { $this->mapper = $mapper; $this->hasher = $hasher; $this->contactsManager = $contactsManager; + $this->userManager = $userManager; $this->groupManager = $groupManager; } @@ -78,6 +83,7 @@ class Backend implements UserInterface, IUserBackend { new GuestMapper(\OC::$server->getDatabaseConnection(), $logger), \OC::$server->getHasher(), \OC::$server->getContactsManager(), + \OC::$server->getUserManager(), \OC::$server->getGroupManager() ); } @@ -259,12 +265,13 @@ class Backend implements UserInterface, IUserBackend { } public function isGuest($uid) { + $conditions = $this->config->getAppValue('guests', 'conditions', 'quota'); + $conditions = explode(',',$conditions); return $uid !== '' && $this->userManager->userExists($uid) && ( - // TODO only check if enabled - $this->isGuestByQuota($uid) || - $this->isGuestByGroup($uid) || - $this->isGuestByContact($uid) + ( in_array('quota', $conditions) && $this->isGuestByQuota($uid) ) || + ( in_array('group', $conditions) && $this->isGuestByGroup($uid) ) || + ( in_array('contact', $conditions) && $this->isGuestByContact($uid) ) ); } @@ -291,11 +298,37 @@ class Backend implements UserInterface, IUserBackend { return false; } public function isGuestByGroup($uid) { - $this->groupManager->isInGroup($uid, 'guests'); + $group = $this->config->getAppValue('guests', 'group', 'guests'); + $this->groupManager->isInGroup($uid, $group); return false; } + + const DEFAULT_GUEST_GROUPS = ',core,settings,avatar,files,files_trashbin,files_versions,files_sharing,files_texteditor,activity'; public function getGuestApps () { - return ['files']; + $apps = $this->config->getAppValue('guests', 'apps', self::DEFAULT_GUEST_GROUPS); + return explode(',', $apps); + } + + /** + * TODO Core has \OC::$REQUESTEDAPP but it isn't set until the routes are matched + * taken from \OC\Route\Router::match() + */ + public function getRequestedApp($url) { + if (substr($url, 0, 6) === '/apps/') { + // empty string / 'apps' / $app / rest of the route + list(, , $app,) = explode('/', $url, 4); + + return \OC_App::cleanAppId($app); + } else if (substr($url, 0, 6) === '/core/') { + return 'core'; + } else if (substr($url, 0, 10) === '/settings/') { + return 'settings'; + } else if (substr($url, 0, 8) === '/avatar/') { + return 'avatar'; + } else if (substr($url, 0, 10) === '/heartbeat') { + return 'heartbeat'; + } + return false; } public function createJail($uid) { diff --git a/lib/hooks.php b/lib/hooks.php index a14d6ac..d8648d5 100644 --- a/lib/hooks.php +++ b/lib/hooks.php @@ -27,8 +27,16 @@ class Hooks { foreach(\OC::$server->getUserManager()->getBackends() as $backend) { if ($backend instanceof Backend && $backend->isGuest($uid)) { - $backend->createJail($uid); + $app = $backend->getRequestedApp(\OC::$server->getRequest()->getRawPathInfo()); + if ( ! in_array($app, $backend->getGuestApps()) ) { + // send forbidden and exit + header('HTTP/1.0 403 Forbidden'); + $l = \OC::$server->getL10NFactory()->get('guests'); + \OCP\Template::printErrorPage($l->t('Access to this resource is forbidden for guests.')); + exit; + } + $backend->createJail($uid); return; } } diff --git a/settings/admin.php b/settings/admin.php new file mode 100644 index 0000000..a1502a0 --- /dev/null +++ b/settings/admin.php @@ -0,0 +1,15 @@ + + * @copyright Jörn Friedrich Dreyer 2015 + */ + +\OCP\Util::addScript('guests', 'guests'); + +$template = new \OCP\Template('guests', 'settings/admin'); +return $template->fetchPage(); diff --git a/templates/settings/admin.php b/templates/settings/admin.php new file mode 100644 index 0000000..1562ebd --- /dev/null +++ b/templates/settings/admin.php @@ -0,0 +1,28 @@ + + * @copyright Jörn Friedrich Dreyer 2015 + */ + +/** @var $l OC_L10N */ +/** @var $_ array */ +?> +
+

Guests

+
+

+
+ +
+
+
+
+
+ +
+