Browse Source

updated CAPTCHA & TOTP module and added CAPTCHATest & TOTPTest

v3-dev
REJack 6 years ago
parent
commit
76a4d788df
No known key found for this signature in database
GPG Key ID: 4A44B48700429F46
  1. 13
      app/Libraries/Aauth/CAPTCHA.php
  2. 60
      app/Libraries/Aauth/TOTP.php
  3. 109
      tests/Aauth/Libraries/Aauth/CAPTCHATest.php
  4. 183
      tests/Aauth/Libraries/Aauth/TOTPTest.php

13
app/Libraries/Aauth/CAPTCHA.php

@ -28,7 +28,6 @@ use \App\Models\Aauth\LoginAttemptModel;
*/ */
class CAPTCHA extends \App\Libraries\Aauth class CAPTCHA extends \App\Libraries\Aauth
{ {
/** /**
* Verify Response * Verify Response
* *
@ -78,17 +77,15 @@ class CAPTCHA extends \App\Libraries\Aauth
$answer = json_decode($request, true); $answer = json_decode($request, true);
if (trim($answer['success']) === true) if (trim($answer['success']) !== true)
{
return ['success' => true];
}
else
{ {
return [ return [
'success' => false, 'success' => false,
'errorCodes' => $answer['error-codes'], 'errorCodes' => $answer['error-codes'],
]; ];
} }
return ['success' => true];
} }
/** /**
@ -110,8 +107,8 @@ class CAPTCHA extends \App\Libraries\Aauth
if ($this->config->captchaType === 'recaptcha') if ($this->config->captchaType === 'recaptcha')
{ {
$content .= "<div class='g-recaptcha' data-sitekey='{$siteKey}'></div>"; $content = "<div class='g-recaptcha' data-sitekey='{$siteKey}'></div>";
$content = '<script src="https://www.google.com/recaptcha/api.js" async defer></script>'; $content .= '<script src="https://www.google.com/recaptcha/api.js" async defer></script>';
} }
else if ($this->config->captchaType === 'hcaptcha') else if ($this->config->captchaType === 'hcaptcha')
{ {

60
app/Libraries/Aauth/TOTP.php

@ -57,19 +57,21 @@ class TOTP extends \App\Libraries\Aauth
*/ */
public function generateUniqueTotpSecret() public function generateUniqueTotpSecret()
{ {
$stop = false; $endSecret = false;
while (! $stop) $userVariableModel = new UserVariableModel();
while (! $endSecret)
{ {
$secret = OTPHP_TOTP::create(); $secret = OTPHP_TOTP::create();
if ($userVariable !== $userVariableModel->where(['data_key' => 'totp_secret', 'data_value' => $secret, 'system' => 1])->getFirstRow('array')) if ($secret->getSecret() !== $userVariableModel->where(['data_key' => 'totp_secret', 'data_value' => $secret->getSecret(), 'system' => 1])->getFirstRow('array'))
{ {
$stop = true; $endSecret = $secret->getSecret();
return $secret;
} }
} }
return $endSecret;
} }
/** /**
@ -81,9 +83,10 @@ class TOTP extends \App\Libraries\Aauth
* *
* @return string * @return string
*/ */
public function generateTotpQrCode(string $secret) public function generateTotpQrCode(string $secret, string $label = '')
{ {
$totp = OTPHP_TOTP::create($secret); $totp = OTPHP_TOTP::create($secret);
$totp->setLabel($label);
return $totp->getQrCodeUri(); return $totp->getQrCodeUri();
} }
@ -98,40 +101,29 @@ class TOTP extends \App\Libraries\Aauth
*/ */
public function verifyUserTotpCode(int $totpCode, string $userId = null) public function verifyUserTotpCode(int $totpCode, string $userId = null)
{ {
if (! $this->isTotpRequired()) if ($this->isTotpRequired())
{
return true;
}
if (! $userId)
{
$userId = (int) @$this->session->user['id'];
}
if (empty($totpCode))
{ {
$this->error(lang('Aauth.requiredTOTPCode')); if (! $userId)
{
return false; $userId = (int) @$this->session->user['id'];
} }
$userVariableModel = new UserVariableModel(); $userVariableModel = new UserVariableModel();
$totpSecret = $userVariableModel->find($userId, 'totp_secret', true); if ($totpSecret = $userVariableModel->find($userId, 'totp_secret', true))
$totp = OTPHP_TOTP::create($totpSecret); {
$totp = OTPHP_TOTP::create($totpSecret);
if (! $totp->verify($totpCode)) if (! $totp->verify($totpCode))
{ {
$this->error(lang('Aauth.invalidTOTPCode')); return false;
}
return false; unset($_SESSION['user']['totp_required']);
}
} }
else
{
unset($_SESSION['user']['totp_required']);
return true; return true;
}
} }
/** /**

109
tests/Aauth/Libraries/Aauth/CAPTCHATest.php

@ -0,0 +1,109 @@
<?php namespace Tests\Aauth\Libraries\Aauth;
use Config\Aauth as AauthConfig;
use Config\App;
use Config\Logger;
use Tests\Support\Log\TestLogger;
use Tests\Support\HTTP\MockResponse;
use Tests\Support\Session\MockSession;
use CodeIgniter\Config\Services;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Session\Handlers\FileHandler;
use CodeIgniter\Test\CIDatabaseTestCase;
use App\Libraries\Aauth;
use App\Models\Aauth\UserVariableModel;
use App\Models\Aauth\LoginTokenModel;
class CAPTCHATest extends CIDatabaseTestCase
{
protected $refresh = true;
protected $basePath = FCPATH . '../app/Database/Migrations';
protected $namespace = 'App';
public function setUp()
{
parent::setUp();
Services::injectMock('response', new MockResponse(new App()));
$this->response = service('response');
$this->request = new IncomingRequest(new App(), new URI(), null, new UserAgent());
Services::injectMock('request', $this->request);
$this->library = new Aauth(null, true);
$_COOKIE = [];
$_SESSION = [];
}
public function tearDown()
{
}
protected function getInstance($options = [])
{
$defaults = [
'sessionDriver' => 'CodeIgniter\Session\Handlers\FileHandler',
'sessionCookieName' => 'ci_session',
'sessionExpiration' => 7200,
'sessionSavePath' => 'null',
'sessionMatchIP' => false,
'sessionTimeToUpdate' => 300,
'sessionRegenerateDestroy' => false,
'cookieDomain' => '',
'cookiePrefix' => '',
'cookiePath' => '/',
'cookieSecure' => false,
];
$config = (object)$defaults;
$session = new MockSession(new FileHandler($config, Services::request()->getIPAddress()), $config);
$session->setLogger(new TestLogger(new Logger()));
$session->start();
return $session;
}
//--------------------------------------------------------------------
public function testGenerateCaptchaHtml()
{
$config = new AauthConfig();
$config->captchaEnabled = true;
$this->library = new Aauth($config, true);
$this->assertEquals('', $this->library->generateCaptchaHtml());
$this->library->login('admina@example.com', 'password123456');
$this->library->login('admina@example.com', 'password123456');
$this->library->login('admina@example.com', 'password123456');
$this->library->login('admina@example.com', 'password123456');
$this->library->login('admina@example.com', 'password123456');
$this->library->login('admina@example.com', 'password123456');
$this->library->login('admina@example.com', 'password123456');
$this->assertContains('https://www.google.com/recaptcha', $this->library->generateCaptchaHtml());
$config->captchaType = 'hcaptcha';
$this->library = new Aauth($config, true);
$this->assertContains('https://hcaptcha.com/1', $this->library->generateCaptchaHtml());
}
public function testVerifyResponse()
{
$config = new AauthConfig();
$config->captchaEnabled = true;
$this->library = new Aauth($config, true);
$this->assertContains('missing-input', $this->library->verifyResponse(null)['errorCodes']);
$this->assertContains('invalid-input-response', $this->library->verifyResponse('0123456789')['errorCodes']);
$config->captchaType = 'hcaptcha';
$this->library = new Aauth($config, true);
$this->assertContains('invalid-input-response', $this->library->verifyResponse('0123456789')['errorCodes']);
}
}

183
tests/Aauth/Libraries/Aauth/TOTPTest.php

@ -0,0 +1,183 @@
<?php namespace Tests\Aauth\Libraries\Aauth;
use Config\Aauth as AauthConfig;
use Config\Logger;
use Config\Services;
use Tests\Support\Log\TestLogger;
use Tests\Support\Session\MockSession;
use CodeIgniter\Session\Handlers\FileHandler;
use CodeIgniter\Test\CIDatabaseTestCase;
use App\Libraries\Aauth;
use App\Models\Aauth\UserVariableModel;
use OTPHP\TOTP;
class TOTPTest extends CIDatabaseTestCase
{
protected $refresh = true;
protected $basePath = FCPATH . '../app/Database/Migrations';
protected $namespace = 'App';
public function setUp()
{
parent::setUp();
$this->library = new Aauth(null, true);
$this->config = new AauthConfig();
$_COOKIE = [];
$_SESSION = [];
}
public function tearDown()
{
}
protected function getInstance($options = [])
{
$defaults = [
'sessionDriver' => 'CodeIgniter\Session\Handlers\FileHandler',
'sessionCookieName' => 'ci_session',
'sessionExpiration' => 7200,
'sessionSavePath' => 'null',
'sessionMatchIP' => false,
'sessionTimeToUpdate' => 300,
'sessionRegenerateDestroy' => false,
'cookieDomain' => '',
'cookiePrefix' => '',
'cookiePath' => '/',
'cookieSecure' => false,
];
$config = (object)$defaults;
$session = new MockSession(new FileHandler($config, Services::request()->getIPAddress()), $config);
$session->setLogger(new TestLogger(new Logger()));
$session->start();
return $session;
}
//--------------------------------------------------------------------
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testUpdateUserTotpSecret()
{
$config = new AauthConfig();
$config->totpEnabled = true;
$this->library = new Aauth($config, true);
$this->assertTrue($this->library->updateUserTotpSecret(99, 'TESTSECRET99'));
$this->seeInDatabase($this->config->dbTableUserVariables, [
'user_id' => 99,
'data_key' => 'totp_secret',
'data_value' => 'TESTSECRET99',
'system' => true,
]);
$session = $this->getInstance();
$this->library = new Aauth($config, $session);
$session->set('user', [
'id' => 1,
'loggedIn' => true,
]);
$this->assertTrue($this->library->updateUserTotpSecret(null, 'TESTSECRET1'));
$this->seeInDatabase($this->config->dbTableUserVariables, [
'user_id' => 1,
'data_key' => 'totp_secret',
'data_value' => 'TESTSECRET1',
'system' => true,
]);
}
public function testGenerateUniqueTotpSecret()
{
$config = new AauthConfig();
$config->totpEnabled = true;
$this->library = new Aauth($config, true);
$this->assertInternalType('string', $this->library->generateUniqueTotpSecret());
}
public function testGenerateTotpQrCode()
{
$config = new AauthConfig();
$config->totpEnabled = true;
$this->library = new Aauth($config, true);
$this->assertInternalType('string', $this->library->generateTotpQrCode('testsecret'));
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testVerifyUserTotpCode()
{
$config = new AauthConfig();
$session = $this->getInstance();
$config->totpEnabled = true;
$this->library = new Aauth($config, $session);
$this->assertTrue($this->library->verifyUserTotpCode('999000', 1));
$this->library = new Aauth($config, $session);
$session->set('user', [
'id' => 1,
'loggedIn' => true,
]);
$this->assertTrue($this->library->verifyUserTotpCode('999000'));
$session = $this->getInstance();
$this->library = new Aauth($config, $session);
$session->set('user', [
'id' => 1,
'loggedIn' => true,
'totp_required' => true,
]);
$this->assertTrue($this->library->verifyUserTotpCode('999000'));
$this->assertTrue($this->library->verifyUserTotpCode('999000', 1));
$this->hasInDatabase($this->config->dbTableUserVariables, [
'user_id' => 1,
'data_key' => 'totp_secret',
'data_value' => 'JBSWY3DPEHPK3PXP',
'system' => true,
]);
$this->assertFalse($this->library->verifyUserTotpCode('999000', 1));
$totp = TOTP::create('JBSWY3DPEHPK3PXP');
$totp->setLabel('');
$this->assertTrue($this->library->verifyUserTotpCode($totp->now(), 1));
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testIsTotpRequired()
{
$config = new AauthConfig();
$config->totpEnabled = true;
$this->library = new Aauth($config, true);
$this->assertFalse($this->library->isTotpRequired());
$session = $this->getInstance();
$this->library = new Aauth($config, $session);
$session->set('user', [
'id' => 1,
'loggedIn' => true,
'totp_required' => true,
]);
$this->assertTrue($this->library->isTotpRequired());
}
}
Loading…
Cancel
Save