add GUI and other small enhancements

This commit is contained in:
Christoph Wurst 2016-06-04 19:07:56 +02:00
Родитель ed7d8759db
Коммит 42156f9b6e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: CC42AC2A7F0E56D8
11 изменённых файлов: 261 добавлений и 39 удалений

Просмотреть файл

@ -19,3 +19,5 @@
*
*/
include_once __DIR__ . '/../vendor/autoload.php';
\OC_App::registerPersonal('twofactor_totp', 'settings/personal');

Просмотреть файл

@ -1,7 +1,5 @@
<?php
namespace OCA\TwoFactorTotp\AppInfo;
/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
@ -20,14 +18,15 @@ namespace OCA\TwoFactorTotp\AppInfo;
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\TwoFactorTotp\AppInfo;
use OCP\AppFramework\App;
class Application extends App {
public function __construct($urlParams = []) {
parent::__construct('twofactor_totps', $urlParams);
$container = $this->getContainer();
}
public function __construct($urlParams = []) {
parent::__construct('twofactor_totps', $urlParams);
}
}

34
appinfo/routes.php Normal file
Просмотреть файл

@ -0,0 +1,34 @@
<?php
/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* ownCloud - Two-factor TOTP
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
return [
'routes' => [
[
'name' => 'settings#state',
'url' => '/settings/state',
'verb' => 'GET'
],
[
'name' => 'settings#enable',
'url' => '/settings/enable',
'verb' => 'POST'
]
]
];

14
js/settings.js Normal file
Просмотреть файл

@ -0,0 +1,14 @@
(function (OC) {
'use strict';
OC.Settings = OC.Settings || {};
OC.Settings.TwoFactorTotp = OC.Settings.TwoFactorTotp || {};
$(function () {
var view = new OC.Settings.TwoFactorTotp.View({
el: $('#twofactor-totp-settings')
});
view.render();
});
})(OC);

89
js/settingsview.js Normal file
Просмотреть файл

@ -0,0 +1,89 @@
/* global Backbone, Handlebars */
(function (OC, Backbone, Handlebars, $) {
'use strict';
OC.Settings = OC.Settings || {};
OC.Settings.TwoFactorTotp = OC.Settings.TwoFactorTotp || {};
var TEMPLATE = '<div>'
+ '<input type="checkbox" class="checkbox" id="totp-enabled">'
+ '<label for="totp-enabled">Enable TOTP</label>'
+ '</div>'
+ '{{#if qr}}'
+ '<div>'
+ '<a href="{{qr}}" target="_blank">Scan QR code with your TOTP app</a><br>'
+ '<img src="{{qr}}>'
+ '</div>'
+ '{{/if}}';
var View = Backbone.View.extend({
template: Handlebars.compile(TEMPLATE),
_loading: undefined,
_enabled: undefined,
events: {
'change #totp-enabled': '_onToggleEnabled'
},
initialize: function () {
this._load();
},
render: function (data) {
this.$el.html(this.template(data));
},
_load: function () {
this._loading = true;
var url = OC.generateUrl('/apps/twofactor_totp/settings/state');
var loading = $.ajax(url, {
method: 'GET',
});
var _this = this;
$.when(loading).done(function (data) {
_this._enabled = data.enabled;
_this.$('#totp-enabled').attr('checked', data.enabled);
});
$.when(loading).always(function () {
_this._loading = false;
});
},
_onToggleEnabled: function () {
if (this._loading) {
// Ignore event
return;
}
var enabled = this.$('#totp-enabled').is(':checked');
if (enabled !== this._enabled) {
this._loading = true;
var url = OC.generateUrl('/apps/twofactor_totp/settings/enable');
var updating = $.ajax(url, {
method: 'POST',
data: {
state: enabled
}
});
var _this = this;
$.when(updating).done(function(data) {
_this._enabled = data.enabled;
_this._showQr(data.qr);
_this.$('#totp-enabled').attr('checked', data.enabled);
});
$.when(updating).always(function () {
_this._loading = false;
});
this._enabled = enabled;
}
},
_showQr: function(qr) {
this.render({
qr: qr
});
}
});
OC.Settings.TwoFactorTotp.View = View;
})(OC, Backbone, Handlebars, $);

Просмотреть файл

@ -0,0 +1,78 @@
<?php
/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* ownCloud - Two-factor TOTP
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\TwoFactorTotp\Controller;
use OCA\TwoFactorTotp\Service\ITotp;
use OCA\TwoFactorTotp\Service\Totp;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
class SettingsController extends Controller {
/** @var ITotp */
private $totp;
/** @var IUserSession */
private $userSession;
public function __construct($appName, IRequest $request, IUserSession $userSession, Totp $totp) {
parent::__construct($appName, $request);
$this->userSession = $userSession;
$this->totp = $totp;
}
/**
* @NoAdminRequired
* @return JSONResponse
*/
public function state() {
$user = $this->userSession->getUser();
return [
'enabled' => $this->totp->hasSecret($user),
];
}
/**
* @NoAdminRequired
* @param bool $state
* @return JSONResponse
*/
public function enable($state) {
$user = $this->userSession->getUser();
if ($state) {
$qr = $this->totp->createSecret($user);
return [
'enabled' => true,
'qr' => $qr,
];
}
$this->totp->deleteSecret($user);
return [
'enabled' => false,
'qr' => null,
];
}
}

Просмотреть файл

@ -39,8 +39,6 @@ class TotpProvider implements IProvider {
/**
* Get unique identifier of this 2FA provider
*
* @since 9.1.0
*
* @return string
*/
public function getId() {
@ -50,10 +48,6 @@ class TotpProvider implements IProvider {
/**
* Get the display name for selecting the 2FA provider
*
* Example: "Email"
*
* @since 9.1.0
*
* @return string
*/
public function getDisplayName() {
@ -63,10 +57,6 @@ class TotpProvider implements IProvider {
/**
* Get the description for selecting the 2FA provider
*
* Example: "Get a token via e-mail"
*
* @since 9.1.0
*
* @return string
*/
public function getDescription() {
@ -76,18 +66,10 @@ class TotpProvider implements IProvider {
/**
* Get the template for rending the 2FA provider view
*
* @since 9.1.0
*
* @param IUser $user
* @return Template
*/
public function getTemplate(IUser $user) {
try {
$this->totp->getSecret($user);
} catch (NoTotpSecretFoundException $ex) {
$qr = $this->totp->createSecret($user);
}
$tmpl = new Template('twofactor_totp', 'challenge');
$tmpl->assign('qr', $qr);
return $tmpl;
@ -96,8 +78,6 @@ class TotpProvider implements IProvider {
/**
* Verify the given challenge
*
* @since 9.1.0
*
* @param IUser $user
* @param string $challenge
*/
@ -108,13 +88,11 @@ class TotpProvider implements IProvider {
/**
* Decides whether 2FA is enabled for the given user
*
* @since 9.1.0
*
* @param IUser $user
* @return boolean
*/
public function isTwoFactorAuthEnabledForUser(IUser $user) {
return true;
return $this->totp->hasSecret($user);
}
}

Просмотреть файл

@ -29,18 +29,22 @@ interface ITotp {
/**
* @param IUser $user
*/
public function getSecret(IUser $user);
public function hasSecret(IUser $user);
/**
* @param IUser $user
* @throws TotpSecretAlreadySet
*/
public function createSecret(IUser $user);
/**
* @param IUser $user
*/
public function deleteSecret(IUser $user);
/**
* @param IUser $user
* @param string $key
*/
public function validateSecret(IUser $user, $key);
}

Просмотреть файл

@ -44,15 +44,13 @@ class Totp implements ITotp {
$this->crypto = $crypto;
}
public function getSecret(IUser $user) {
public function hasSecret(IUser $user) {
try {
$secret = $this->secretMapper->getSecret($user);
$this->secretMapper->getSecret($user);
} catch (DoesNotExistException $ex) {
throw new NoTotpSecretFoundException();
return false;
}
$encryptedSecret = $secret->getSecret();
return $this->crypto->decrypt($encryptedSecret);
return true;
}
/**
@ -72,6 +70,16 @@ class Totp implements ITotp {
return GoogleAuthenticator::getQrCodeUrl('totp', 'ownCloud TOTP', $secret);
}
public function deleteSecret(IUser $user) {
try {
// TODO: execute DELETE sql in mapper instead
$dbSecret = $this->secretMapper->getSecret($user);
$this->secretMapper->delete($dbSecret);
} catch (DoesNotExistException $ex) {
}
}
public function validateSecret(IUser $user, $key) {
try {
$dbSecret = $this->secretMapper->getSecret($user);

5
settings/personal.php Normal file
Просмотреть файл

@ -0,0 +1,5 @@
<?php
$tmpl = new \OCP\Template('twofactor_totp', 'personal');
return $tmpl->fetchPage();

11
templates/personal.php Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<?php
script('twofactor_totp', 'settingsview');
script('twofactor_totp', 'settings');
?>
<div class="section">
<h2>TOTP Second-factor Auth</h2>
<div id="twofactor-totp-settings"></div>
</div>