Skip to content

Commit

Permalink
Merge pull request #269 from nextcloud/enh/100/api-with-token
Browse files Browse the repository at this point in the history
Allow API endpoint access with a predefined token
  • Loading branch information
PVince81 authored Mar 26, 2021
2 parents 0e439d0 + 44ecf6f commit 87bf21e
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 0 deletions.
55 changes: 55 additions & 0 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,28 @@
use OCA\ServerInfo\ShareStatistics;
use OCA\ServerInfo\StorageStatistics;
use OCA\ServerInfo\SystemStatistics;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IUserSession;

class ApiController extends OCSController {

/** @var Os */
private $os;

/** @var IConfig */
private $config;

/** @var IGroupManager */
private $groupManager;

/** @var IUserSession */
private $userSession;

/** @var SystemStatistics */
private $systemStatistics;

Expand All @@ -60,6 +73,9 @@ class ApiController extends OCSController {
*
* @param string $appName
* @param IRequest $request
* @param IConfig $config
* @param IGroupManager $groupManager
* @param IUserSession $userSession
* @param Os $os
* @param SystemStatistics $systemStatistics
* @param StorageStatistics $storageStatistics
Expand All @@ -70,6 +86,9 @@ class ApiController extends OCSController {
*/
public function __construct($appName,
IRequest $request,
IConfig $config,
IGroupManager $groupManager,
?IUserSession $userSession,
Os $os,
SystemStatistics $systemStatistics,
StorageStatistics $storageStatistics,
Expand All @@ -79,6 +98,9 @@ public function __construct($appName,
SessionStatistics $sessionStatistics) {
parent::__construct($appName, $request);

$this->config = $config;
$this->groupManager = $groupManager;
$this->userSession = $userSession;
$this->os = $os;
$this->systemStatistics = $systemStatistics;
$this->storageStatistics = $storageStatistics;
Expand All @@ -88,12 +110,45 @@ public function __construct($appName,
$this->sessionStatistics = $sessionStatistics;
}

private function checkAuthorized() {
$token = $this->request->getHeader('NC-Token');
if (!empty($token)) {
$storedToken = $this->config->getAppValue('serverinfo', 'token', null);
if (hash_equals($storedToken, $token)) {
return true;
}
}

$userSession = $this->userSession;
if ($userSession === null) {
return false;
}

$user = $userSession->getUser();
if ($user === null) {
return false;
}

if (!$this->groupManager->isAdmin($user->getUID())) {
return false;
};

return true;
}

/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @return DataResponse
*/
public function info() {
if (!$this->checkAuthorized()) {
$response = new DataResponse(['message' => 'Unauthorized']);
$response->setStatus(Http::STATUS_UNAUTHORIZED);
return $response;
}
return new DataResponse([
'nextcloud' => [
'system' => $this->systemStatistics->getSystemStatistics(),
Expand Down
7 changes: 7 additions & 0 deletions templates/settings-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,13 @@ class="barchart"
<p class="settings-hint">
<?php p($l->t('Appending "?format=json" at the end of the URL gives you the result in JSON.')); ?>
</p>
<p>
<?php p($l->t('To use an access token please generate one then set it using the following command:')); ?>
<div><i>occ config:app:set serverinfo token --value yourtoken</i></div>
</p>
<p>
<?php p($l->t('Then pass the token with the "NC-Token" header when querying the above URL.')); ?>
</p>
</div>
</div>
</div>
Expand Down
243 changes: 243 additions & 0 deletions tests/lib/ApiControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
<?php
/**
* @copyright Copyright (c) 2021 Vincent Petry <vincent@nextcloud.com>
*
* @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 OCA\ServerInfo\Tests;

use OCA\ServerInfo\Controller\ApiController;
use OCA\ServerInfo\DatabaseStatistics;
use OCA\ServerInfo\Os;
use OCA\ServerInfo\PhpStatistics;
use OCA\ServerInfo\SessionStatistics;
use OCA\ServerInfo\ShareStatistics;
use OCA\ServerInfo\StorageStatistics;
use OCA\ServerInfo\SystemStatistics;
use OCP\AppFramework\Http;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;

class ApiControllerTest extends \Test\TestCase {
/** @var Os|\PHPUnit\Framework\MockObject\MockObject */
private $os;

/** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */
private $request;

/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $config;

/** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
private $groupManager;

/** @var SystemStatistics|\PHPUnit\Framework\MockObject\MockObject */
private $systemStatistics;

/** @var StorageStatistics|\PHPUnit\Framework\MockObject\MockObject */
private $storageStatistics;

/** @var PhpStatistics|\PHPUnit\Framework\MockObject\MockObject */
private $phpStatistics;

/** @var DatabaseStatistics|\PHPUnit\Framework\MockObject\MockObject */
private $databaseStatistics;

/** @var ShareStatistics|\PHPUnit\Framework\MockObject\MockObject */
private $shareStatistics;

/** @var SessionStatistics|\PHPUnit\Framework\MockObject\MockObject */
private $sessionStatistics;

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

$this->request = $this->createMock(IRequest::class);
$this->config = $this->createMock(IConfig::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->os = $this->createMock(Os::class);
$this->systemStatistics = $this->createMock(SystemStatistics::class);
$this->storageStatistics = $this->createMock(StorageStatistics::class);
$this->phpStatistics = $this->createMock(PhpStatistics::class);
$this->databaseStatistics = $this->createMock(DatabaseStatistics::class);
$this->shareStatistics = $this->createMock(ShareStatistics::class);
$this->sessionStatistics = $this->createMock(SessionStatistics::class);
}

private function getController($userSession) {
return new ApiController(
'serverinfo',
$this->request,
$this->config,
$this->groupManager,
$userSession,
$this->os,
$this->systemStatistics,
$this->storageStatistics,
$this->phpStatistics,
$this->databaseStatistics,
$this->shareStatistics,
$this->sessionStatistics
);
}

public function testAuthFailureNoSession() {
$response = $this->getController(null)->info();

$this->assertEquals(['message' => 'Unauthorized'], $response->getData());
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
}

public function testAuthFailureNoUser() {
$userSession = $this->createMock(IUserSession::class);
$userSession->method('getUser')->willReturn(null);

$response = $this->getController($userSession)->info();

$this->assertEquals(['message' => 'Unauthorized'], $response->getData());
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
}

public function testAuthFailureNoAdmin() {
$userSession = $this->createMock(IUserSession::class);
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('nonadmin');
$userSession->method('getUser')->willReturn($user);
$this->groupManager->expects($this->once())
->method('isAdmin')
->with('nonadmin')
->willReturn(false);

$response = $this->getController($userSession)->info();

$this->assertEquals(['message' => 'Unauthorized'], $response->getData());
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
}

public function testAuthSuccessWithAdmin() {
$userSession = $this->createMock(IUserSession::class);
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('admin');
$userSession->method('getUser')->willReturn($user);
$this->groupManager->expects($this->once())
->method('isAdmin')
->with('admin')
->willReturn(true);

$response = $this->getController($userSession)->info();

$this->assertEquals(Http::STATUS_OK, $response->getStatus());
}

public function testAuthFailureWithToken() {
$this->request->expects($this->once())
->method('getHeader')
->with('NC-Token')
->willReturn('invalidtoken');

$this->config->expects($this->once())
->method('getAppValue')
->with('serverinfo', 'token', null)
->willReturn('megatoken');
$response = $this->getController(null)->info();

$this->assertEquals(['message' => 'Unauthorized'], $response->getData());
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
}

public function testAuthSuccessWithToken() {
$this->request->expects($this->once())
->method('getHeader')
->with('NC-Token')
->willReturn('megatoken');

$this->config->expects($this->once())
->method('getAppValue')
->with('serverinfo', 'token', null)
->willReturn('megatoken');
$response = $this->getController(null)->info();

$this->assertEquals(Http::STATUS_OK, $response->getStatus());
}

public function testIn() {
$this->request->expects($this->once())
->method('getHeader')
->with('NC-Token')
->willReturn('megatoken');

$this->config->expects($this->once())
->method('getAppValue')
->with('serverinfo', 'token', null)
->willReturn('megatoken');
$response = $this->getController(null)->info();

$this->assertEquals(Http::STATUS_OK, $response->getStatus());
}

public function testInfo() {
$userSession = $this->createMock(IUserSession::class);
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('admin');
$userSession->method('getUser')->willReturn($user);
$this->groupManager->expects($this->once())
->method('isAdmin')
->with('admin')
->willReturn(true);

$this->systemStatistics->expects($this->once())
->method('getSystemStatistics')
->willReturn(['some_system' => 'some_value']);
$this->storageStatistics->expects($this->once())
->method('getStorageStatistics')
->willReturn(['some_storage' => 'some_value']);
$this->shareStatistics->expects($this->once())
->method('getShareStatistics')
->willReturn(['some_shares' => 'some_value']);
$this->phpStatistics->expects($this->once())
->method('getPhpStatistics')
->willReturn(['some_php' => 'some_value']);
$this->databaseStatistics->expects($this->once())
->method('getDatabaseStatistics')
->willReturn(['some_database' => 'some_value']);
$this->sessionStatistics->expects($this->once())
->method('getSessionStatistics')
->willReturn(['some_user' => 'some_value']);

$response = $this->getController($userSession)->info();

$this->assertEquals(Http::STATUS_OK, $response->getStatus());

$this->assertEquals([
'nextcloud' => [
'system' => ['some_system' => 'some_value'],
'storage' => ['some_storage' => 'some_value'],
'shares' => ['some_shares' => 'some_value'],
],
'server' => [
'webserver' => 'unknown',
'php' => ['some_php' => 'some_value'],
'database' => ['some_database' => 'some_value'],
],
'activeUsers' => ['some_user' => 'some_value'],
], $response->getData());
}
}

0 comments on commit 87bf21e

Please sign in to comment.