Add two factor authentication
This commit is contained in:
parent
5d393ed996
commit
abeeba7167
|
|
@ -84,6 +84,7 @@ Documentation
|
|||
#### Working with users
|
||||
|
||||
- [User management](docs/user-management.markdown)
|
||||
- [Two factor authentication](docs/2fa.markdown)
|
||||
|
||||
#### Settings
|
||||
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ abstract class Base
|
|||
|
||||
if (! $this->acl->isPublicAction($controller, $action)) {
|
||||
$this->handleAuthentication();
|
||||
$this->handle2FA($controller, $action);
|
||||
$this->handleAuthorization($controller, $action);
|
||||
|
||||
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
|
||||
|
|
@ -199,6 +200,26 @@ abstract class Base
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 2FA
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function handle2FA($controller, $action)
|
||||
{
|
||||
$controllers = array('twofactor', 'user');
|
||||
$actions = array('code', 'check', 'logout');
|
||||
|
||||
if ($this->userSession->has2FA() && ! $this->userSession->check2FA() && ! in_array($controller, $controllers) && ! in_array($action, $actions)) {
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url('twofactor', 'code', array('user_id' => $user['id'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check page access and authorization
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Otp\Otp;
|
||||
use Otp\GoogleAuthenticator;
|
||||
use Base32\Base32;
|
||||
|
||||
/**
|
||||
* Two Factor Auth controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Twofactor extends User
|
||||
{
|
||||
/**
|
||||
* Only the current user can access to 2FA settings
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function checkCurrentUser(array $user)
|
||||
{
|
||||
if ($user['id'] != $this->userSession->getId()) {
|
||||
$this->forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCurrentUser($user);
|
||||
|
||||
$label = $user['email'] ?: $user['username'];
|
||||
|
||||
$this->response->html($this->layout('twofactor/index', array(
|
||||
'user' => $user,
|
||||
'qrcode_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getQrCodeUrl('totp', $label, $user['twofactor_secret']) : '',
|
||||
'key_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getKeyUri('totp', $label, $user['twofactor_secret']) : '',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable 2FA
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCurrentUser($user);
|
||||
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (isset($values['twofactor_activated']) && $values['twofactor_activated'] == 1) {
|
||||
$this->user->update(array(
|
||||
'id' => $user['id'],
|
||||
'twofactor_activated' => 1,
|
||||
'twofactor_secret' => GoogleAuthenticator::generateRandom(),
|
||||
));
|
||||
}
|
||||
else {
|
||||
$this->user->update(array(
|
||||
'id' => $user['id'],
|
||||
'twofactor_activated' => 0,
|
||||
'twofactor_secret' => '',
|
||||
));
|
||||
}
|
||||
|
||||
$this->session->flash(t('User updated successfully.'));
|
||||
$this->response->redirect($this->helper->url('twofactor', 'index', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2FA
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCurrentUser($user);
|
||||
|
||||
$otp = new Otp;
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
|
||||
$this->session->flash(t('The two factor authentication code is valid.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('The two factor authentication code is not valid.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url('twofactor', 'index', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 2FA
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCurrentUser($user);
|
||||
|
||||
$otp = new Otp;
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
|
||||
$this->session['2fa_validated'] = true;
|
||||
$this->session->flash(t('The two factor authentication code is valid.'));
|
||||
$this->response->redirect($this->helper->url('app', 'index'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('The two factor authentication code is not valid.'));
|
||||
$this->response->redirect($this->helper->url('twofactor', 'code'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the 2FA code
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function code()
|
||||
{
|
||||
$this->response->html($this->template->layout('twofactor/check', array(
|
||||
'title' => t('Check two factor authentication code'),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -836,4 +836,15 @@ return array(
|
|||
'Help on Hipchat integration' => 'Aide sur l\'intégration avec Hipchat',
|
||||
'Enable Gravatar images' => 'Activer les images Gravatar',
|
||||
'Information' => 'Informations',
|
||||
'Check two factor authentication code' => 'Vérification du code pour l\'authentification à deux-facteurs',
|
||||
'The two factor authentication code is not valid.' => 'Le code pour l\'authentification à deux-facteurs n\'est pas valide.',
|
||||
'The two factor authentication code is valid.' => 'Le code pour l\'authentification à deux-facteurs est valide.',
|
||||
'Code' => 'Code',
|
||||
'Two factor authentication' => 'Authentification à deux-facteurs',
|
||||
'Enable/disable two factor authentication' => 'Activer/désactiver l\'authentification à deux-facteurs',
|
||||
'This QR Ccde contains the key URI: ' => 'Ce code QR contient l\'url de la clé : ',
|
||||
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => 'Sauvegardez cette clé secrete dans votre logiciel TOTP (par exemple Google Authenticator ou FreeOTP).',
|
||||
'Check my code' => 'Vérifier mon code',
|
||||
'Secret key: ' => 'Clé secrète : ',
|
||||
'Test your device' => 'Testez votre appareil',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -834,4 +834,15 @@ return array(
|
|||
// 'Help on Hipchat integration' => '',
|
||||
// 'Enable Gravatar images' => '',
|
||||
// 'Information' => '',
|
||||
// 'Check two factor authentication code' => '',
|
||||
// 'The two factor authentication code is not valid.' => '',
|
||||
// 'The two factor authentication code is valid.' => '',
|
||||
// 'Code' => '',
|
||||
// 'Two factor authentication' => '',
|
||||
// 'Enable/disable two factor authentication' => '',
|
||||
// 'This QR Ccde contains the key URI: ' => '',
|
||||
// 'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => '',
|
||||
// 'Check my code' => '',
|
||||
// 'Secret key: ' => '',
|
||||
// 'Test your device' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ class User extends Base
|
|||
'is_ldap_user',
|
||||
'notifications_enabled',
|
||||
'google_id',
|
||||
'github_id'
|
||||
'github_id',
|
||||
'twofactor_activated'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,14 +28,41 @@ class UserSession extends Base
|
|||
unset($user['password']);
|
||||
}
|
||||
|
||||
if (isset($user['twofactor_secret'])) {
|
||||
unset($user['twofactor_secret']);
|
||||
}
|
||||
|
||||
$user['id'] = (int) $user['id'];
|
||||
$user['default_project_id'] = (int) $user['default_project_id'];
|
||||
$user['is_admin'] = (bool) $user['is_admin'];
|
||||
$user['is_ldap_user'] = (bool) $user['is_ldap_user'];
|
||||
$user['twofactor_activated'] = (bool) $user['twofactor_activated'];
|
||||
|
||||
$this->session['user'] = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the user has validated the 2FA key
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function check2FA()
|
||||
{
|
||||
return isset($this->session['2fa_validated']) && $this->session['2fa_validated'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the user has 2FA enabled
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function has2FA()
|
||||
{
|
||||
return isset($this->session['user']['twofactor_activated']) && $this->session['user']['twofactor_activated'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the logged user is admin
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ use PDO;
|
|||
use Core\Security;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 60;
|
||||
const VERSION = 61;
|
||||
|
||||
function version_61($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated TINYINT(1) DEFAULT 0');
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret CHAR(16)');
|
||||
}
|
||||
|
||||
function version_60($pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ use PDO;
|
|||
use Core\Security;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 40;
|
||||
const VERSION = 42;
|
||||
|
||||
function version_42($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated BOOLEAN DEFAULT \'0\'');
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret CHAR(16)');
|
||||
}
|
||||
|
||||
function version_41($pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ use Core\Security;
|
|||
use PDO;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 59;
|
||||
const VERSION = 60;
|
||||
|
||||
function version_60($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated INTEGER DEFAULT 0');
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret TEXT');
|
||||
}
|
||||
|
||||
function version_59($pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<form method="post" action="<?= $this->u('twofactor', 'check', array('user_id' => $this->userSession->getId())) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->formCsrf() ?>
|
||||
<?= $this->formLabel(t('Code'), 'code') ?>
|
||||
<?= $this->formText('code', array(), array(), array('placeholder="123456"'), 'form-numeric') ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Check my code') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Two factor authentication') ?></h2>
|
||||
</div>
|
||||
|
||||
<form method="post" action="<?= $this->u('twofactor', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->formCsrf() ?>
|
||||
<?= $this->formCheckbox('twofactor_activated', t('Enable/disable two factor authentication'), 1, isset($user['twofactor_activated']) && $user['twofactor_activated'] == 1) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php if ($user['twofactor_activated'] == 1): ?>
|
||||
<div class="listing">
|
||||
<p><?= t('Secret key: ') ?><strong><?= $this->e($user['twofactor_secret']) ?></strong> (base32)</p>
|
||||
<p><br/><img src="<?= $qrcode_url ?>"/><br/><br/></p>
|
||||
<p>
|
||||
<?= t('This QR Ccde contains the key URI: ') ?><strong><?= $this->e($key_url) ?></strong>
|
||||
<br/><br/>
|
||||
<?= t('Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).') ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3><?= t('Test your device') ?></h3>
|
||||
<form method="post" action="<?= $this->u('twofactor', 'test', array('user_id' => $user['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->formCsrf() ?>
|
||||
<?= $this->formLabel(t('Code'), 'code') ?>
|
||||
<?= $this->formText('code', array(), array(), array('placeholder="123456"'), 'form-numeric') ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Check my code') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
<th><?= $paginator->order(t('Name'), 'name') ?></th>
|
||||
<th><?= $paginator->order(t('Email'), 'email') ?></th>
|
||||
<th><?= $paginator->order(t('Administrator'), 'is_admin') ?></th>
|
||||
<th><?= $paginator->order(t('Two factor authentication'), 'twofactor_activated') ?></th>
|
||||
<th><?= $paginator->order(t('Default project'), 'default_project_id') ?></th>
|
||||
<th><?= $paginator->order(t('Notifications'), 'notifications_enabled') ?></th>
|
||||
<th><?= t('External accounts') ?></th>
|
||||
|
|
@ -39,6 +40,9 @@
|
|||
<td>
|
||||
<?= $user['is_admin'] ? t('Yes') : t('No') ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $user['twofactor_activated'] ? t('Yes') : t('No') ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= (isset($user['default_project_id']) && isset($projects[$user['default_project_id']])) ? $this->e($projects[$user['default_project_id']]) : t('None'); ?>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,32 @@
|
|||
<div class="sidebar">
|
||||
<h2><?= t('Actions') ?></h2>
|
||||
<h2><?= t('Information') ?></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<?= $this->a(t('Summary'), 'user', 'show', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php if ($this->userSession->isAdmin()): ?>
|
||||
<li>
|
||||
<?= $this->a(t('User dashboard'), 'app', 'dashboard', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('User calendar'), 'user', 'calendar', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($this->userSession->isAdmin() || $this->userSession->isCurrentUser($user['id'])): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Time tracking'), 'user', 'timesheet', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Last logins'), 'user', 'last', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Persistent connections'), 'user', 'sessions', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
|
||||
<h2><?= t('Actions') ?></h2>
|
||||
<ul>
|
||||
<?php if ($this->userSession->isAdmin() || $this->userSession->isCurrentUser($user['id'])): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Edit profile'), 'user', 'edit', array('user_id' => $user['id'])) ?>
|
||||
|
|
@ -16,30 +38,21 @@
|
|||
</li>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($this->userSession->isCurrentUser($user['id'])): ?>
|
||||
<li>
|
||||
<?= $this->a(t('Two factor authentication'), 'twofactor', 'index', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
|
||||
<li>
|
||||
<?= $this->a(t('Email notifications'), 'user', 'notifications', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('External accounts'), 'user', 'external', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Last logins'), 'user', 'last', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Persistent connections'), 'user', 'sessions', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Time tracking'), 'user', 'timesheet', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($this->userSession->isAdmin()): ?>
|
||||
<li>
|
||||
<?= $this->a(t('User dashboard'), 'app', 'dashboard', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('User calendar'), 'user', 'calendar', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Hourly rates'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@
|
|||
"pimple/pimple" : "~3.0",
|
||||
"symfony/console" : "@stable",
|
||||
"symfony/event-dispatcher" : "~2.6",
|
||||
"fguillot/simpleLogger" : "0.0.1"
|
||||
"fguillot/simpleLogger" : "0.0.1",
|
||||
"christian-riesen/otp": "1.4"
|
||||
},
|
||||
"autoload" : {
|
||||
"psr-0" : {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,110 @@
|
|||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "9c0c8b06745d54478604d80815b59b9b",
|
||||
"hash": "466ee3928d7d3b0bbee15de3b4c76676",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christian-riesen/base32",
|
||||
"version": "1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ChristianRiesen/base32.git",
|
||||
"reference": "6943e7b010ab224139fd4248ccf84865aa7b0f91"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/6943e7b010ab224139fd4248ccf84865aa7b0f91",
|
||||
"reference": "6943e7b010ab224139fd4248ccf84865aa7b0f91",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*",
|
||||
"satooshi/php-coveralls": "0.*"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Base32\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Riesen",
|
||||
"email": "chris.riesen@gmail.com",
|
||||
"homepage": "http://christianriesen.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Base32 encoder/decoder according to RFC 4648",
|
||||
"homepage": "https://github.com/ChristianRiesen/base32",
|
||||
"keywords": [
|
||||
"base32",
|
||||
"decode",
|
||||
"encode",
|
||||
"rfc4648"
|
||||
],
|
||||
"time": "2015-02-12 09:08:33"
|
||||
},
|
||||
{
|
||||
"name": "christian-riesen/otp",
|
||||
"version": "1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ChristianRiesen/otp.git",
|
||||
"reference": "a209b8bbd975d96d6b5287f8658562061adef1f8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ChristianRiesen/otp/zipball/a209b8bbd975d96d6b5287f8658562061adef1f8",
|
||||
"reference": "a209b8bbd975d96d6b5287f8658562061adef1f8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"christian-riesen/base32": ">=1.0",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Otp": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Riesen",
|
||||
"email": "chris.riesen@gmail.com",
|
||||
"homepage": "http://christianriesen.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "One Time Passwords, hotp and totp according to RFC4226 and RFC6238",
|
||||
"homepage": "https://github.com/ChristianRiesen/otp",
|
||||
"keywords": [
|
||||
"googleauthenticator",
|
||||
"hotp",
|
||||
"otp",
|
||||
"rfc4226",
|
||||
"rfc6238",
|
||||
"totp"
|
||||
],
|
||||
"time": "2015-02-12 09:11:49"
|
||||
},
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"version": "1.5.1",
|
||||
|
|
@ -571,6 +673,7 @@
|
|||
},
|
||||
"prefer-stable": false,
|
||||
"platform": {
|
||||
"php": ">=5.3",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
Two factor authentication
|
||||
=========================
|
||||
|
||||
Each user can enable the [two factor authentication](http://en.wikipedia.org/wiki/Two_factor_authentication).
|
||||
After a successful login, a one-time code (6 characters) is asked to the user to allow the access to Kanboard.
|
||||
|
||||
This code have to be provided by a compatible software generally installed on your smartphone.
|
||||
|
||||
Kanboard use the [Time-based One-time Password Algorithm](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) defined in the [RFC 6238](http://tools.ietf.org/html/rfc6238).
|
||||
|
||||
There are many software compatible with the standard TOTP system.
|
||||
By example, you can use these free and open source applications:
|
||||
|
||||
- [Google Authenticator](https://github.com/google/google-authenticator/) (Android, iOS, Blackberry)
|
||||
- [FreeOTP](https://fedorahosted.org/freeotp/) (Android, iOS)
|
||||
- [OATH Toolkit](http://www.nongnu.org/oath-toolkit/) (Command line utility on Unix/Linux)
|
||||
|
||||
This system can work offline and you don't necessary need to have a mobile phone.
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
1. Go to your user profile
|
||||
2. On the left, click on **Two factor authentication** and check the box
|
||||
3. A secret key is generated for you
|
||||
|
||||

|
||||
|
||||
- You have to save the secret key in your TOTP software. If you use a smartphone, the easiest solution is to scan the QR code with FreeOTP or Google Authenticator.
|
||||
- Each time you will open a new session, a new code will be asked
|
||||
- Don't forget to test your device before closing your session
|
||||
|
||||
A new secret key is generated each time you enable/disable this feature.
|
||||
Loading…
Reference in New Issue