diff options
Diffstat (limited to 'app')
28 files changed, 475 insertions, 20 deletions
diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 6420e0ee..f498c3ce 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -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()); @@ -200,6 +201,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 * * @access public diff --git a/app/Controller/Twofactor.php b/app/Controller/Twofactor.php new file mode 100644 index 00000000..7711666b --- /dev/null +++ b/app/Controller/Twofactor.php @@ -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'), + ))); + } +} diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 6d08a885..51397129 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -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' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 5bc1663c..82319766 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -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' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 80de8774..4fa07781 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -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' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 842086bb..239d5a51 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -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' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index ef53801e..7cca5aa6 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -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', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index cb23c082..af5c8cb7 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -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' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index b0d38c8e..b4cdf9f0 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -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' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 2573fbf9..ff563073 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -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' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 4cc4a5fa..eba573d2 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -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' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index e02d1f2b..bad64238 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -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' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 2483e142..787aa2d0 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -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' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 86cc1531..bcf216cc 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -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' => '', ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index e6bdd7c2..0229a030 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -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' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 4dc75556..3e52818b 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -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' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index 1000cf3f..400c0c26 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -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' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index 62d404eb..0f8a2802 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -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' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 4cb288ad..5f3eacf0 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -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' => '', ); diff --git a/app/Model/User.php b/app/Model/User.php index 7586f3c4..6c348caa 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -60,7 +60,8 @@ class User extends Base 'is_ldap_user', 'notifications_enabled', 'google_id', - 'github_id' + 'github_id', + 'twofactor_activated' ); } diff --git a/app/Model/UserSession.php b/app/Model/UserSession.php index 6d9a2ebc..efb02722 100644 --- a/app/Model/UserSession.php +++ b/app/Model/UserSession.php @@ -28,15 +28,42 @@ 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 * * @access public diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 4ea7b041..2dcfd3b7 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -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) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index 93d8e869..6c6ea001 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -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) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 3683acb6..ef6523e4 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -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) { diff --git a/app/Template/twofactor/check.php b/app/Template/twofactor/check.php new file mode 100644 index 00000000..af64bc76 --- /dev/null +++ b/app/Template/twofactor/check.php @@ -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>
\ No newline at end of file diff --git a/app/Template/twofactor/index.php b/app/Template/twofactor/index.php new file mode 100644 index 00000000..bc9a12e9 --- /dev/null +++ b/app/Template/twofactor/index.php @@ -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 ?> diff --git a/app/Template/user/index.php b/app/Template/user/index.php index 41e205ba..d6b0fecf 100644 --- a/app/Template/user/index.php +++ b/app/Template/user/index.php @@ -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> @@ -40,6 +41,9 @@ <?= $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> <td> diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php index 1af10c1d..f794c609 100644 --- a/app/Template/user/sidebar.php +++ b/app/Template/user/sidebar.php @@ -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,31 +38,22 @@ </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> <li> |