allow users to enable/disable U2F via their personal settings

This commit is contained in:
Christoph Wurst 2016-08-26 15:26:23 +02:00
Родитель b2ecea2c82
Коммит 022e8ea063
4 изменённых файлов: 123 добавлений и 26 удалений

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

@ -19,16 +19,26 @@
* *
*/ */
return [ return [
'routes' => [ 'routes' => [
[ [
'name' => 'settings#startRegister', 'name' => 'settings#state',
'url' => '/settings/startregister', 'url' => '/settings/state',
'verb' => 'POST' 'verb' => 'GET'
], ],
[ [
'name' => 'settings#finishRegister', 'name' => 'settings#disable',
'url' => '/settings/finishregister', 'url' => '/settings/disable',
'verb' => 'POST' 'verb' => 'POST'
], ],
] [
'name' => 'settings#startRegister',
'url' => '/settings/startregister',
'verb' => 'POST'
],
[
'name' => 'settings#finishRegister',
'url' => '/settings/finishregister',
'verb' => 'POST'
],
]
]; ];

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

@ -1,49 +1,107 @@
/* global Backbone, Handlebars, OC, u2f */ /* global Backbone, Handlebars, OC, u2f */
(function (OC, Backbone, Handlebars, $, u2f) { (function(OC, Backbone, Handlebars, $, u2f) {
'use strict'; 'use strict';
OC.Settings = OC.Settings || {}; OC.Settings = OC.Settings || {};
OC.Settings.TwoFactorU2F = OC.Settings.TwoFactorU2F || {}; OC.Settings.TwoFactorU2F = OC.Settings.TwoFactorU2F || {};
var TEMPLATE = '<div>' var TEMPLATE = '<div>'
+ ' <button id="u2f-register">Register</button>' + ' <input type="checkbox" class="checkbox" id="u2f-enabled" {{#if enabled}}checked{{/if}}>'
+ ' <label for="u2f-enabled">' + t('twofactor_u2f', 'Use U2F device') + '</label>'
+ '</div>'; + '</div>';
var View = Backbone.View.extend({ var View = Backbone.View.extend({
template: Handlebars.compile(TEMPLATE), _template: undefined,
_enabled: undefined,
template: function(data) {
if (!this._template) {
this._template = Handlebars.compile(TEMPLATE);
}
return this._template(data);
},
events: { events: {
'click #u2f-register': 'onRegister' 'change #u2f-enabled': '_onToggleEnabled',
}, },
initialize: function () { initialize: function() {
this._load();
},
render: function() {
this.$el.html(this.template({
enabled: this._enabled
}));
},
_load: function() {
var url = OC.generateUrl('/apps/twofactor_u2f/settings/state');
$.ajax(url, {
method: 'GET',
}).done(function(data) {
this._enabled = data.enabled;
this.render();
}.bind(this)).fail(function() {
OC.Notification.showTemporary('Could not get U2F enabled/disabled state.');
});
},
_onToggleEnabled: function() {
if (this._loading) {
// Ignore event
return;
}
var enabled = this.$('#u2f-enabled').is(':checked');
if (enabled === this._enabled) {
console.log('ign');
return;
}
this._enabled = enabled;
if (enabled) {
this._onRegister();
} else {
this._onDisable();
}
}, },
render: function (data) { _onRegister: function() {
this.$el.html(this.template(data)); this._loading = true;
},
onRegister: function () {
console.log('start register…'); console.log('start register…');
var url = OC.generateUrl('apps/twofactor_u2f/settings/startregister'); var url = OC.generateUrl('apps/twofactor_u2f/settings/startregister');
$.ajax(url, { $.ajax(url, {
method: 'POST' method: 'POST'
}).done(function (data) { }).done(function(data) {
this.doRegister(data.req, data.sigs); this.doRegister(data.req, data.sigs);
}.bind(this)).fail(function() { }.bind(this)).fail(function() {
OC.Notification.showTemporary('server error while trying to add U2F device'); OC.Notification.showTemporary('server error while trying to add U2F device');
}); }).always(function() {
this._loading = false;
}.bind(this));
}, },
doRegister: function (req, sigs) { _onDisable: function() {
this._loading = true;
console.log('disabling U2F…');
var url = OC.generateUrl('apps/twofactor_u2f/settings/disable');
$.ajax(url, {
method: 'POST'
}).fail(function() {
OC.Notification.showTemporary('Could not disable U2F');
}.bind(this)).always(function() {
this._loading = false;
}.bind(this));
},
doRegister: function(req, sigs) {
console.log('doRegister', req, sigs); console.log('doRegister', req, sigs);
u2f.register([req], sigs, function (data) { u2f.register([req], sigs, function(data) {
console.log(data.errorCode); console.log(data.errorCode);
if (data.errorCode && data.errorCode !== 0) { if (data.errorCode && data.errorCode !== 0) {
OC.Notification.showTemporary('U2F device registration failed (error code ' + data.errorCode + ')'); OC.Notification.showTemporary('U2F device registration failed (error code ' + data.errorCode + ')');
this._enabled = false;
this.render();
return; return;
} }
this.finishRegister(data); this.finishRegister(data);
}.bind(this)); }.bind(this));
}, },
finishRegister: function (data) { finishRegister: function(data) {
console.log('finish register…', data); console.log('finish register…', data);
var url = OC.generateUrl('apps/twofactor_u2f/settings/finishregister'); var url = OC.generateUrl('apps/twofactor_u2f/settings/finishregister');
$.ajax(url, { $.ajax(url, {

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

@ -27,12 +27,34 @@ class SettingsController extends Controller {
/** @var IUserSession */ /** @var IUserSession */
private $userSession; private $userSession;
/**
* @param string $appName
* @param IRequest $request
* @param U2FManager $manager
* @param IUserSession $userSession
*/
public function __construct($appName, IRequest $request, U2FManager $manager, IUserSession $userSession) { public function __construct($appName, IRequest $request, U2FManager $manager, IUserSession $userSession) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->manager = $manager; $this->manager = $manager;
$this->userSession = $userSession; $this->userSession = $userSession;
} }
/**
* @NoAdminRequired
*/
public function state() {
return [
'enabled' => $this->manager->isEnabled($this->userSession->getUser())
];
}
/**
* @NoAdminRequired
*/
public function disable() {
$this->manager->disableU2F($this->userSession->getUser());
}
/** /**
* @NoAdminRequired * @NoAdminRequired
* @UseSession * @UseSession

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

@ -62,6 +62,13 @@ class U2FManager {
return count($registrations) > 0; return count($registrations) > 0;
} }
public function disableU2F(IUser $user) {
// TODO: use single query instead
foreach ($this->mapper->findRegistrations($user) as $registration) {
$this->mapper->delete($registration);
}
}
public function startRegistration(IUser $user) { public function startRegistration(IUser $user) {
$u2f = $this->getU2f(); $u2f = $this->getU2f();
$data = $u2f->getRegisterData($this->getRegistrations($user)); $data = $u2f->getRegisterData($this->getRegistrations($user));