Skip to content

Commit

Permalink
add validate api for totp
Browse files Browse the repository at this point in the history
  • Loading branch information
karakayasemi committed Feb 25, 2019
1 parent 5ca4c26 commit 7abb5d7
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 8 deletions.
11 changes: 11 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,16 @@
'url' => '/settings/verifyNewSecret',
'verb' => 'POST'
],
],
'ocs' => [
[
'name' => 'totp_api#validateKey',
'url' => '/api/v1/validate/{uid}/{key}',
'verb' => 'GET',
'requirements' => [
'uid' => '.+',
'key' => '.+'
]
]
]
];
82 changes: 82 additions & 0 deletions lib/Controller/TotpApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/**
* @author Semih Serhat Karakaya <karakayasemi@itu.edu.tr>
*
* 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\TwoFactor_Totp\Controller;

use OCA\TwoFactor_Totp\Exception\NoTotpSecretFoundException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\ILogger;
use \OCP\IRequest;
use \OCP\IUserManager;
use OCA\TwoFactor_Totp\Service\ITotp;

class TotpApiController extends OCSController {

/** @var ITotp */
private $totp;

/** @var IUserManager */
private $userManager;

/** @var ILogger */
private $logger;

public function __construct(
$appName,
IRequest $request,
ITotp $totp,
IUserManager $userManager,
ILogger $logger
) {
parent::__construct($appName, $request);
$this->totp = $totp;
$this->userManager = $userManager;
$this->logger = $logger;
}

/**
* @CORS
* @NoCSRFRequired
*
* @param string $uid
* @param string $key 6 digits numeric time-based one time password.
* @return DataResponse
*/
public function validateKey($uid, $key) {
$user = $this->userManager->get($uid);
if ($user !== null) {
try {
return new DataResponse(['data' => ['result' => $this->totp->validateKey($user, $key)]]);
} catch (NoTotpSecretFoundException $e) {
$this->logger->logException($e);
}
}
return new DataResponse(
[
'statuscode' => 404,
'data' => ['result' => false]
],
Http::STATUS_NOT_FOUND
);
}
}
2 changes: 1 addition & 1 deletion lib/Provider/TotpProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public function getTemplate(IUser $user) {
* @param string $challenge
*/
public function verifyChallenge(IUser $user, $challenge) {
return $this->totp->validateSecret($user, $challenge);
return $this->totp->validateKey($user, $challenge);
}

/**
Expand Down
7 changes: 5 additions & 2 deletions lib/Service/ITotp.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

namespace OCA\TwoFactor_Totp\Service;

use OCA\TwoFactor_Totp\Exception\NoTotpSecretFoundException;
use OCA\TwoFactor_Totp\Exception\TotpSecretAlreadySet;
use OCP\IUser;

Expand All @@ -46,9 +47,11 @@ public function deleteSecret(IUser $user);

/**
* @param IUser $user
* @param string $key
* @param string $key 6 digits numeric time-based one time password.
* @return boolean If key is correct
* @throws NoTotpSecretFoundException
*/
public function validateSecret(IUser $user, $key);
public function validateKey(IUser $user, $key);

/**
* @param IUser $user
Expand Down
6 changes: 4 additions & 2 deletions lib/Service/Totp.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public function deleteSecret(IUser $user) {
* @param string $key
*/
public function verifySecret(IUser $user, $key) {
if ($this->validateSecret($user, $key) === true) {
if ($this->validateKey($user, $key) === true) {
$dbSecret = $this->secretMapper->getSecret($user);
$dbSecret->setVerified(true);
$this->secretMapper->update($dbSecret);
Expand All @@ -104,8 +104,10 @@ public function verifySecret(IUser $user, $key) {
/**
* @param IUser $user
* @param string $key
* @return boolean
* @throws NoTotpSecretFoundException
*/
public function validateSecret(IUser $user, $key) {
public function validateKey(IUser $user, $key) {
try {
$dbSecret = $this->secretMapper->getSecret($user);
} catch (DoesNotExistException $ex) {
Expand Down
138 changes: 138 additions & 0 deletions tests/unit/Controller/TotpApiControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php

/**
* @author Semih Serhat Karakaya <karakayasemi@itu.edu.tr>
*
* 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\TwoFactor_Totp\Unit\Controller;

use OC\OCS\Result;
use OCA\TwoFactor_Totp\Controller\TotpApiController;
use OCA\TwoFactor_Totp\Exception\NoTotpSecretFoundException;
use OCA\TwoFactor_Totp\Service\ITotp;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\ILogger;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserManager;
use Test\TestCase;
use PHPUnit_Framework_MockObject_MockObject;

class TotpApiControllerTest extends TestCase {

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

/** @var IUserManager | PHPUnit_Framework_MockObject_MockObject */
private $userManager;

/** @var IUser | PHPUnit_Framework_MockObject_MockObject */
private $user;

/** @var ITotp | PHPUnit_Framework_MockObject_MockObject */
private $totp;

/** @var ILogger | PHPUnit_Framework_MockObject_MockObject */
private $logger;

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

protected function setUp() {
parent::setUp();
$this->user = $this->createMock(IUser::class);
$this->request = $this->createMock(IRequest::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->totp = $this->createMock(ITotp::class);
$this->logger = $this->createMock(ILogger::class);

$this->controller = new TotpApiController(
'twofactor_totp',
$this->request, $this->totp,
$this->userManager,
$this->logger
);
}

/**
* @dataProvider dataTestValidateKey
*
* @param string $uid
* @param boolean $result
*/
public function testValidateKey($uid, $result) {
$this->userManager->expects($this->once())
->method('get')
->with($uid)
->will($this->returnValue($this->user));
$this->totp->expects($this->once())
->method('validateKey')
->with($this->user, '111111')
->will($this->returnValue($result));

$expected = new DataResponse(['data' => ['result' => $result]]);
$this->assertEquals($expected, $this->controller->validateKey($uid, '111111'));
}

public function dataTestValidateKey() {
return [
['testuser', false],
['testuser', true],
];
}

public function testValidateKeyUserNotExist() {
$this->userManager->expects($this->once())
->method('get')
->with('notexist')
->will($this->returnValue(null));
$expected = new DataResponse(
[
'statuscode' => 404,
'data' => ['result' => false]
],
Http::STATUS_NOT_FOUND
);
$this->assertEquals($expected, $this->controller->validateKey('notexist', '111111'));
}

public function testValidateKeySecretNotExist() {
$exception = new NoTotpSecretFoundException();
$this->user = $this->createMock(IUser::class);
$this->userManager->expects($this->once())
->method('get')
->with('testuser')
->will($this->returnValue($this->user));
$this->totp->expects($this->once())
->method('validateKey')
->with($this->user, '111111')
->will($this->throwException($exception));
$this->logger->expects($this->once())
->method('logException')
->with($exception);
$expected = new DataResponse(
[
'statuscode' => 404,
'data' => ['result' => false]
],
Http::STATUS_NOT_FOUND
);
$this->assertEquals($expected, $this->controller->validateKey('testuser', '111111'));
}
}
62 changes: 62 additions & 0 deletions tests/unit/Provider/TotpProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
/**
* @author Semih Serhat Karakaya <karakayasemi@itu.edu.tr>
*
* 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\TwoFactor_Totp\Tests\Provider;

use OCA\TwoFactor_Totp\Provider\TotpProvider;
use OCA\TwoFactor_Totp\Service\ITotp;
use OCP\IL10N;
use OCP\IUser;
use Test\TestCase;

/**
* Class TotpTest
*/
class TotpProviderTest extends TestCase {

/** @var ITotp | \PHPUnit_Framework_MockObject_MockObject $totp */
private $totp;

/** @var IL10N | \PHPUnit_Framework_MockObject_MockObject */
private $l;

/** @var IUser | \PHPUnit_Framework_MockObject_MockObject */
private $user;

/** @var TotpProvider $totpProvider */
private $totpProvider;

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

$this->totp = $this->createMock(ITotp::class);
$this->l = $this->createMock(IL10N::class);
$this->user = $this->createMock(IUser::class);

$this->totpProvider = new TotpProvider($this->totp, $this->l);
}

public function testVerifyChallange() {
$this->totp->expects($this->once())
->method('validateKey')
->with($this->user, '111111');
$this->totpProvider->verifyChallenge($this->user, '111111');
}
}
6 changes: 3 additions & 3 deletions tests/unit/Service/TotpTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected function setUp() {
* @param boolean $validationResult
* @param boolean $expectedResult
*/
public function testValidateSecret($lastKey, $key, $validationResult, $expectedResult) {
public function testValidateKey($lastKey, $key, $validationResult, $expectedResult) {
/** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->createMock(IUser::class);
$dbSecret = $this
Expand Down Expand Up @@ -96,7 +96,7 @@ public function testValidateSecret($lastKey, $key, $validationResult, $expectedR
->method('update')
->with($dbSecret);
}
$this->assertEquals($this->totp->validateSecret($user, $key), $expectedResult);
$this->assertEquals($this->totp->validateKey($user, $key), $expectedResult);
}

public function validationProvider() {
Expand All @@ -117,6 +117,6 @@ public function testValidateSecretNoSecret() {
->with($user)
->will($this->throwException(new DoesNotExistException('')));
$this->expectException(NoTotpSecretFoundException::class);
$this->totp->validateSecret($user, 'testkey');
$this->totp->validateKey($user, 'testkey');
}
}

0 comments on commit 7abb5d7

Please sign in to comment.