From 12431aa3997154aaea4eec11c2dd65f9e5dbe179 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Wed, 18 May 2016 12:03:22 +0200 Subject: [PATCH] list user's auth tokens on the personal settings page --- .../Authentication/Token/DefaultToken.php | 12 ++- lib/private/Server.php | 3 +- settings/Application.php | 16 +++- .../Controller/AuthSettingsController.php | 71 ++++++++++++++ settings/css/settings.css | 15 +++ settings/js/authtoken.js | 33 +++++++ settings/js/authtoken_collection.js | 36 +++++++ settings/js/authtoken_view.js | 93 +++++++++++++++++++ settings/js/personal.js | 7 ++ settings/personal.php | 5 + settings/routes.php | 3 +- settings/templates/personal.php | 30 ++++++ .../controller/AuthSettingsControllerTest.php | 66 +++++++++++++ 13 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 settings/Controller/AuthSettingsController.php create mode 100644 settings/js/authtoken.js create mode 100644 settings/js/authtoken_collection.js create mode 100644 settings/js/authtoken_view.js create mode 100644 tests/settings/controller/AuthSettingsControllerTest.php diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php index 08451a4615..ca4c723fba 100644 --- a/lib/private/Authentication/Token/DefaultToken.php +++ b/lib/private/Authentication/Token/DefaultToken.php @@ -22,6 +22,7 @@ namespace OC\Authentication\Token; +use JsonSerializable; use OCP\AppFramework\Db\Entity; /** @@ -38,7 +39,7 @@ use OCP\AppFramework\Db\Entity; * @method void setLastActivity(int $lastActivity) * @method int getLastActivity() */ -class DefaultToken extends Entity implements IToken { +class DefaultToken extends Entity implements IToken, JsonSerializable { /** * @var string user UID @@ -87,4 +88,13 @@ class DefaultToken extends Entity implements IToken { return parent::getPassword(); } + public function jsonSerialize() { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'lastActivity' => $this->lastActivity, + 'type' => $this->type, + ]; + } + } diff --git a/lib/private/Server.php b/lib/private/Server.php index 0b7b8f9e40..ea0c436d84 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -223,6 +223,7 @@ class Server extends ServerContainer implements IServerContainer { $timeFactory = new TimeFactory(); return new \OC\Authentication\Token\DefaultTokenProvider($mapper, $crypto, $config, $logger, $timeFactory); }); + $this->registerAlias('OC\Authentication\Token\IProvider', 'OC\Authentication\Token\DefaultTokenProvider'); $this->registerService('UserSession', function (Server $c) { $manager = $c->getUserManager(); $session = new \OC\Session\Memory(''); @@ -230,7 +231,7 @@ class Server extends ServerContainer implements IServerContainer { // Token providers might require a working database. This code // might however be called when ownCloud is not yet setup. if (\OC::$server->getSystemConfig()->getValue('installed', false)) { - $defaultTokenProvider = $c->query('OC\Authentication\Token\DefaultTokenProvider'); + $defaultTokenProvider = $c->query('OC\Authentication\Token\IProvider'); } else { $defaultTokenProvider = null; } diff --git a/settings/Application.php b/settings/Application.php index 5b84d028ab..7069fc9c35 100644 --- a/settings/Application.php +++ b/settings/Application.php @@ -29,7 +29,9 @@ namespace OC\Settings; use OC\Files\View; +use OC\Server; use OC\Settings\Controller\AppSettingsController; +use OC\Settings\Controller\AuthSettingsController; use OC\Settings\Controller\CertificateController; use OC\Settings\Controller\CheckSetupController; use OC\Settings\Controller\EncryptionController; @@ -39,10 +41,9 @@ use OC\Settings\Controller\MailSettingsController; use OC\Settings\Controller\SecuritySettingsController; use OC\Settings\Controller\UsersController; use OC\Settings\Middleware\SubadminMiddleware; -use \OCP\AppFramework\App; +use OCP\AppFramework\App; use OCP\IContainer; -use \OCP\Util; -use OC\Server; +use OCP\Util; /** * @package OC\Settings @@ -97,6 +98,15 @@ class Application extends App { $c->query('OcsClient') ); }); + $container->registerService('AuthSettingsController', function(IContainer $c) { + return new AuthSettingsController( + $c->query('AppName'), + $c->query('Request'), + $c->query('ServerContainer')->query('OC\Authentication\Token\IProvider'), + $c->query('UserManager'), + $c->query('UserId') + ); + }); $container->registerService('SecuritySettingsController', function(IContainer $c) { return new SecuritySettingsController( $c->query('AppName'), diff --git a/settings/Controller/AuthSettingsController.php b/settings/Controller/AuthSettingsController.php new file mode 100644 index 0000000000..1d874193d3 --- /dev/null +++ b/settings/Controller/AuthSettingsController.php @@ -0,0 +1,71 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Settings\Controller; + +use OC\Authentication\Token\IProvider; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IRequest; +use OCP\IUserManager; + +class AuthSettingsController extends Controller { + + /** @var IProvider */ + private $tokenProvider; + + /** + * @var IUserManager + */ + private $userManager; + + /** @var string */ + private $uid; + + /** + * @param string $appName + * @param IRequest $request + * @param IProvider $tokenProvider + * @param IUserManager $userManager + * @param string $uid + */ + public function __construct($appName, IRequest $request, IProvider $tokenProvider, IUserManager $userManager, $uid) { + parent::__construct($appName, $request); + $this->tokenProvider = $tokenProvider; + $this->userManager = $userManager; + $this->uid = $uid; + } + + /** + * @NoAdminRequired + * + * @return JSONResponse + */ + public function index() { + $user = $this->userManager->get($this->uid); + if (is_null($user)) { + return []; + } + return $this->tokenProvider->getTokenByUser($user); + } + +} diff --git a/settings/css/settings.css b/settings/css/settings.css index edc4939d2d..be61265935 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -100,6 +100,21 @@ input#identity { table.nostyle label { margin-right: 2em; } table.nostyle td { padding: 0.2em 0; } +#sessions, +#devices { + min-height: 180px; +} +#sessions table, +#devices table { + width: 100%; + min-height: 150px; + padding-top: 25px; +} +#sessions table th, +#devices table th { + font-weight: 800; +} + /* USERS */ #newgroup-init a span { margin-left: 20px; } #newgroup-init a span:before { diff --git a/settings/js/authtoken.js b/settings/js/authtoken.js new file mode 100644 index 0000000000..215192d716 --- /dev/null +++ b/settings/js/authtoken.js @@ -0,0 +1,33 @@ +/* global Backbone */ + +/** + * @author Christoph Wurst + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +(function(OC, Backbone) { + 'use strict'; + + OC.Settings = OC.Settings || {}; + + var AuthToken = Backbone.Model.extend({ + }); + + OC.Settings.AuthToken = AuthToken; + +})(OC, Backbone); diff --git a/settings/js/authtoken_collection.js b/settings/js/authtoken_collection.js new file mode 100644 index 0000000000..dd964356d0 --- /dev/null +++ b/settings/js/authtoken_collection.js @@ -0,0 +1,36 @@ +/* global Backbone */ + +/** + * @author Christoph Wurst + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +(function(OC, Backbone) { + 'use strict'; + + OC.Settings = OC.Settings || {}; + + var AuthTokenCollection = Backbone.Collection.extend({ + model: OC.Settings.AuthToken, + tokenType: null, + url: OC.generateUrl('/settings/personal/authtokens'), + }); + + OC.Settings.AuthTokenCollection = AuthTokenCollection; + +})(OC, Backbone); diff --git a/settings/js/authtoken_view.js b/settings/js/authtoken_view.js new file mode 100644 index 0000000000..0ca1682123 --- /dev/null +++ b/settings/js/authtoken_view.js @@ -0,0 +1,93 @@ +/* global Backbone, Handlebars */ + +/** + * @author Christoph Wurst + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +(function(OC, _, Backbone, $, Handlebars) { + 'use strict'; + + OC.Settings = OC.Settings || {}; + + var TEMPLATE_TOKEN = + '' + + '{{name}}' + + '{{lastActivity}}' + + ''; + + var SubView = Backbone.View.extend({ + collection: null, + type: 0, + template: Handlebars.compile(TEMPLATE_TOKEN), + initialize: function(options) { + this.type = options.type; + this.collection = options.collection; + }, + render: function() { + var _this = this; + + var list = this.$el.find('.token-list'); + var tokens = this.collection.filter(function(token) { + return parseInt(token.get('type')) === _this.type; + }); + list.removeClass('icon-loading'); + list.html(''); + + tokens.forEach(function(token) { + var html = _this.template(token.toJSON()); + list.append(html); + }); + }, + }); + + var AuthTokenView = Backbone.View.extend({ + collection: null, + views + : [], + initialize: function(options) { + this.collection = options.collection; + + var tokenTypes = [0, 1]; + var _this = this; + _.each(tokenTypes, function(type) { + _this.views.push(new SubView({ + el: type === 0 ? '#sessions' : '#devices', + type: type, + collection: _this.collection + })); + }); + }, + render: function() { + _.each(this.views, function(view) { + view.render(); + }); + }, + reload: function() { + var loadingTokens = this.collection.fetch(); + + var _this = this; + $.when(loadingTokens).done(function() { + _this.render(); + }); + } + }); + + OC.Settings.AuthTokenView = AuthTokenView; + +})(OC, _, Backbone, $, Handlebars); diff --git a/settings/js/personal.js b/settings/js/personal.js index 09f63f3f6a..aea2400e99 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -361,6 +361,13 @@ $(document).ready(function () { if (oc_config.enable_avatars) { $('#avatar .avatardiv').avatar(OC.currentUser, 145); } + + // Show token views + var collection = new OC.Settings.AuthTokenCollection(); + var view = new OC.Settings.AuthTokenView({ + collection: collection + }); + view.reload(); }); if (!OC.Encryption) { diff --git a/settings/personal.php b/settings/personal.php index 6c2fccbec9..3b283fb2d3 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -42,6 +42,9 @@ $config = \OC::$server->getConfig(); $urlGenerator = \OC::$server->getURLGenerator(); // Highlight navigation entry +OC_Util::addScript('settings', 'authtoken'); +OC_Util::addScript('settings', 'authtoken_collection'); +OC_Util::addScript('settings', 'authtoken_view'); OC_Util::addScript( 'settings', 'personal' ); OC_Util::addScript('settings', 'certificates'); OC_Util::addStyle( 'settings', 'settings' ); @@ -171,6 +174,8 @@ $tmpl->assign('groups', $groups2); // add hardcoded forms from the template $formsAndMore = []; $formsAndMore[]= ['anchor' => 'avatar', 'section-name' => $l->t('Personal info')]; +$formsAndMore[]= ['anchor' => 'sessions', 'section-name' => $l->t('Sessions')]; +$formsAndMore[]= ['anchor' => 'devices', 'section-name' => $l->t('Devices')]; $formsAndMore[]= ['anchor' => 'clientsbox', 'section-name' => $l->t('Sync clients')]; $forms=OC_App::getForms('personal'); diff --git a/settings/routes.php b/settings/routes.php index 90e1d1e442..5c356e0173 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -36,7 +36,8 @@ $application = new Application(); $application->registerRoutes($this, [ 'resources' => [ 'groups' => ['url' => '/settings/users/groups'], - 'users' => ['url' => '/settings/users/users'] + 'users' => ['url' => '/settings/users/users'], + 'AuthSettings' => ['url' => '/settings/personal/authtokens'], ], 'routes' => [ ['name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'], diff --git a/settings/templates/personal.php b/settings/templates/personal.php index 29bf240e7e..a7e86b50a5 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -139,6 +139,36 @@ if($_['passwordChangeSupported']) { } ?> +
+

t('Sessions'));?>

+ t('These are the web browsers currently logged in to your ownCloud.'));?> + + + + + + + + + +
BrowserMost recent activity
+
+ +
+

t('Devices'));?>

+ t("You've linked these devices."));?> + + + + + + + + + +
NameMost recent activity
+
+

diff --git a/tests/settings/controller/AuthSettingsControllerTest.php b/tests/settings/controller/AuthSettingsControllerTest.php new file mode 100644 index 0000000000..d236f9f5eb --- /dev/null +++ b/tests/settings/controller/AuthSettingsControllerTest.php @@ -0,0 +1,66 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace Test\Settings\Controller; + +use OC\Settings\Controller\AuthSettingsController; +use Test\TestCase; + +class AuthSettingsControllerTest extends TestCase { + + /** @var AuthSettingsController */ + private $controller; + private $request; + private $tokenProvider; + private $userManager; + private $uid; + + protected function setUp() { + parent::setUp(); + + $this->request = $this->getMock('\OCP\IRequest'); + $this->tokenProvider = $this->getMock('\OC\Authentication\Token\IProvider'); + $this->userManager = $this->getMock('\OCP\IUserManager'); + $this->uid = 'jane'; + $this->user = $this->getMock('\OCP\IUser'); + + $this->controller = new AuthSettingsController('core', $this->request, $this->tokenProvider, $this->userManager, $this->uid); + } + + public function testIndex() { + $result = [ + 'token1', + 'token2', + ]; + $this->userManager->expects($this->once()) + ->method('get') + ->with($this->uid) + ->will($this->returnValue($this->user)); + $this->tokenProvider->expects($this->once()) + ->method('getTokenByUser') + ->with($this->user) + ->will($this->returnValue($result)); + + $this->assertEquals($result, $this->controller->index()); + } + +}