Skip to content

Commit

Permalink
Merge pull request #11675 from nextcloud/feature/enforce-2fa-admin-se…
Browse files Browse the repository at this point in the history
…ttings

Add admin interface to enforce 2FA
  • Loading branch information
rullzer authored Oct 10, 2018
2 parents 0acae1d + 67c3730 commit 2a690db
Show file tree
Hide file tree
Showing 27 changed files with 1,005 additions and 27 deletions.
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,7 @@
'OC\\Settings\\Controller\\LogSettingsController' => $baseDir . '/settings/Controller/LogSettingsController.php',
'OC\\Settings\\Controller\\MailSettingsController' => $baseDir . '/settings/Controller/MailSettingsController.php',
'OC\\Settings\\Controller\\PersonalSettingsController' => $baseDir . '/settings/Controller/PersonalSettingsController.php',
'OC\\Settings\\Controller\\TwoFactorSettingsController' => $baseDir . '/settings/Controller/TwoFactorSettingsController.php',
'OC\\Settings\\Controller\\UsersController' => $baseDir . '/settings/Controller/UsersController.php',
'OC\\Settings\\Hooks' => $baseDir . '/settings/Hooks.php',
'OC\\Settings\\Mailer\\NewUserMailHelper' => $baseDir . '/settings/Mailer/NewUserMailHelper.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Settings\\Controller\\LogSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/LogSettingsController.php',
'OC\\Settings\\Controller\\MailSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/MailSettingsController.php',
'OC\\Settings\\Controller\\PersonalSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/PersonalSettingsController.php',
'OC\\Settings\\Controller\\TwoFactorSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/TwoFactorSettingsController.php',
'OC\\Settings\\Controller\\UsersController' => __DIR__ . '/../../..' . '/settings/Controller/UsersController.php',
'OC\\Settings\\Hooks' => __DIR__ . '/../../..' . '/settings/Hooks.php',
'OC\\Settings\\Mailer\\NewUserMailHelper' => __DIR__ . '/../../..' . '/settings/Mailer/NewUserMailHelper.php',
Expand Down
63 changes: 63 additions & 0 deletions settings/Controller/TwoFactorSettingsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

/**
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OC\Settings\Controller;

use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use OCP\JSON;

class TwoFactorSettingsController extends Controller {

/** @var MandatoryTwoFactor */
private $mandatoryTwoFactor;

public function __construct(string $appName,
IRequest $request,
MandatoryTwoFactor $mandatoryTwoFactor) {
parent::__construct($appName, $request);

$this->mandatoryTwoFactor = $mandatoryTwoFactor;
}

public function index(): Response {
return new JSONResponse([
'enabled' => $this->mandatoryTwoFactor->isEnforced(),
]);
}

public function update(bool $enabled): Response {
$this->mandatoryTwoFactor->setEnforced($enabled);

return new JSONResponse([
'enabled' => $enabled
]);
}

}
8 changes: 4 additions & 4 deletions settings/js/0.settings-vue.js → settings/js/0.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion settings/js/0.settings-vue.js.map → settings/js/0.js.map

Large diffs are not rendered by default.

303 changes: 303 additions & 0 deletions settings/js/1.js

Large diffs are not rendered by default.

411 changes: 411 additions & 0 deletions settings/js/2.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion settings/js/2.settings-vue.js.map

This file was deleted.

4 changes: 2 additions & 2 deletions settings/js/2.settings-vue.js → settings/js/3.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions settings/js/3.js.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion settings/js/3.settings-vue.js.map

This file was deleted.

4 changes: 2 additions & 2 deletions settings/js/3.settings-vue.js → settings/js/4.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions settings/js/4.js.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion settings/js/4.settings-vue.js.map

This file was deleted.

6 changes: 3 additions & 3 deletions settings/js/4.settings-vue.js → settings/js/5.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions settings/js/5.js.map

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions settings/js/settings-admin-security.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions settings/js/settings-admin-security.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions settings/js/settings-vue.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion settings/js/settings-vue.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions settings/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@
['name' => 'AdminSettings#index', 'url' => '/settings/admin/{section}', 'verb' => 'GET', 'defaults' => ['section' => 'server']],
['name' => 'AdminSettings#form', 'url' => '/settings/admin/{section}', 'verb' => 'GET'],
['name' => 'ChangePassword#changePersonalPassword', 'url' => '/settings/personal/changepassword', 'verb' => 'POST'],
['name' => 'ChangePassword#changeUserPassword', 'url' => '/settings/users/changepassword', 'verb' => 'POST']
['name' => 'ChangePassword#changeUserPassword', 'url' => '/settings/users/changepassword', 'verb' => 'POST'],
['name' => 'TwoFactorSettings#index', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'GET'],
['name' => 'TwoFactorSettings#update', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'PUT'],
]
]);

Expand Down
76 changes: 76 additions & 0 deletions settings/src/components/AdminTwoFactor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<div>
<p>
{{ t('settings', 'Two-factor authentication can be enforced for all users. If they do not have a two-factor provider configured, they will be unable to log into the system.') }}
</p>
<p v-if="loading">
<span class="icon-loading-small two-factor-loading"></span>
<span>{{ t('settings', 'Enforce two-factor authentication') }}</span>
</p>
<p v-else>
<input type="checkbox"
id="two-factor-enforced"
class="checkbox"
v-model="enabled"
v-on:change="onEnforcedChanged">
<label for="two-factor-enforced">{{ t('settings', 'Enforce two-factor authentication') }}</label>
</p>
</div>
</template>

<script>
import Axios from 'nextcloud-axios'
export default {
name: "AdminTwoFactor",
data () {
return {
enabled: false,
loading: false
}
},
mounted () {
this.loading = true
Axios.get(OC.generateUrl('/settings/api/admin/twofactorauth'))
.then(resp => resp.data)
.then(state => {
this.enabled = state.enabled
this.loading = false
console.info('loaded')
})
.catch(err => {
console.error(error)
this.loading = false
throw err
})
},
methods: {
onEnforcedChanged () {
this.loading = true
const data = {
enabled: this.enabled
}
Axios.put(OC.generateUrl('/settings/api/admin/twofactorauth'), data)
.then(resp => resp.data)
.then(state => {
this.enabled = state.enabled
this.loading = false
})
.catch(err => {
console.error(error)
this.loading = false
throw err
})
}
}
}
</script>

<style>
.two-factor-loading {
display: inline-block;
vertical-align: sub;
margin-left: -2px;
margin-right: 1px;
}
</style>
13 changes: 13 additions & 0 deletions settings/src/main-admin-security.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Vue from 'vue'

import AdminTwoFactor from './components/AdminTwoFactor'

Vue.prototype.t = t;

new Vue({
el: '#two-factor-auth-settings',
template: '<AdminTwoFactor/>',
components: {
AdminTwoFactor
}
})
7 changes: 7 additions & 0 deletions settings/templates/settings/admin/security.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,15 @@
/** @var \OCP\IL10N $l */
/** @var array $_ */

script('settings', 'settings-admin-security');

?>

<div id="two-factor-auth" class="section">
<h2><?php p($l->t('Two-Factor Authentication'));?></h2>
<div id="two-factor-auth-settings"></div>
</div>

<div class="section" id='encryptionAPI'>
<h2><?php p($l->t('Server-side encryption')); ?></h2>
<a target="_blank" rel="noreferrer noopener" class="icon-info"
Expand Down
7 changes: 5 additions & 2 deletions settings/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ const path = require('path')
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
entry: './src/main.js',
entry: {
'settings-vue': './src/main.js',
'settings-admin-security': './src/main-admin-security'
},
output: {
path: path.resolve(__dirname, './js'),
publicPath: '/',
filename: 'settings-vue.js'
filename: '[name].js'
},
module: {
rules: [
Expand Down
82 changes: 82 additions & 0 deletions tests/Settings/Controller/TwoFactorSettingsControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

namespace Tests\Settings\Controller;

use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor;
use OC\Settings\Controller\TwoFactorSettingsController;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;

class TwoFactorSettingsControllerTest extends TestCase {

/** @var IRequest|MockObject */
private $request;

/** @var MandatoryTwoFactor|MockObject */
private $mandatoryTwoFactor;

/** @var TwoFactorSettingsController */
private $controller;

protected function setUp() {
parent::setUp();

$this->request = $this->createMock(IRequest::class);
$this->mandatoryTwoFactor = $this->createMock(MandatoryTwoFactor::class);

$this->controller = new TwoFactorSettingsController(
'settings',
$this->request,
$this->mandatoryTwoFactor
);
}

public function testIndex() {
$this->mandatoryTwoFactor->expects($this->once())
->method('isEnforced')
->willReturn(true);
$expected = new JSONResponse([
'enabled' => true,
]);

$resp = $this->controller->index();

$this->assertEquals($expected, $resp);
}

public function testUpdate() {
$this->mandatoryTwoFactor->expects($this->once())
->method('setEnforced')
->with(true);
$expected = new JSONResponse([
'enabled' => true,
]);

$resp = $this->controller->update(true);

$this->assertEquals($expected, $resp);
}

}

0 comments on commit 2a690db

Please sign in to comment.