diff --git a/inc/Plugins/Authenticator/ActionController.class.php b/inc/Plugins/Authenticator/ActionController.class.php index d123fba1..e082932d 100644 --- a/inc/Plugins/Authenticator/ActionController.class.php +++ b/inc/Plugins/Authenticator/ActionController.class.php @@ -31,6 +31,7 @@ use SP\Core\Session as CoreSession; use SP\DataModel\PluginData; use SP\Http\Request; use SP\Mgmt\Plugins\Plugin; +use SP\Util\ArrayUtil; use SP\Util\Checks; use SP\Util\Json; @@ -99,8 +100,11 @@ class ActionController implements ItemControllerInterface protected function save() { $pin = Request::analyze('security_pin', 0); + $twofa_enabled = Request::analyze('security_2faenabled', 0, false, 1); - $twoFa = new Authenticator($this->itemId, CoreSession::getUserData()->getUserLogin()); + $AuthenticatorData = Session::getUserData(); + + $twoFa = new Authenticator($this->itemId, CoreSession::getUserData()->getUserLogin(), $AuthenticatorData->getIV()); if (!$twoFa->verifyKey($pin)) { $this->jsonResponse->setDescription(_('Código incorrecto')); @@ -114,16 +118,17 @@ class ActionController implements ItemControllerInterface $data = $this->Plugin->getData(); - if (!isset($data[$this->itemId])) { - $data[$this->itemId] = new AuthenticatorData(); - } + if ($twofa_enabled) { + /** @var AuthenticatorData $AuthenticatorData */ + $AuthenticatorData->setUserId($this->itemId); + $AuthenticatorData->setTwofaEnabled($twofa_enabled); + $AuthenticatorData->setExpireDays(Request::analyze('expiredays', 0)); + $AuthenticatorData->setDate(time()); - /** @var AuthenticatorData $AuthenticatorData */ - $AuthenticatorData = $data[$this->itemId]; - $AuthenticatorData->setUserId($this->itemId); - $AuthenticatorData->setTwofaEnabled(Request::analyze('security_2faenabled', 0, false, 1)); - $AuthenticatorData->setExpireDays(Request::analyze('expiredays', 0)); - $AuthenticatorData->setDate(time()); + $data[$this->itemId] = $AuthenticatorData; + } elseif (!$twofa_enabled) { + unset($data[$this->itemId]); + } $PluginData = new PluginData(); $PluginData->setPluginName($this->Plugin->getName()); @@ -149,7 +154,11 @@ class ActionController implements ItemControllerInterface $userId = Request::analyze('itemId', 0); $pin = Request::analyze('security_pin', 0); - $TwoFa = new Authenticator($userId); + // Buscar al usuario en los datos del plugin + /** @var AuthenticatorData $AuthenticatorData */ + $AuthenticatorData = ArrayUtil::searchInObject($this->Plugin->getData(), 'userId', $userId, new AuthenticatorData()); + + $TwoFa = new Authenticator($userId, null, $AuthenticatorData->getIV()); if ($userId && $pin diff --git a/inc/Plugins/Authenticator/Authenticator.class.php b/inc/Plugins/Authenticator/Authenticator.class.php index bc732df9..4133d3af 100644 --- a/inc/Plugins/Authenticator/Authenticator.class.php +++ b/inc/Plugins/Authenticator/Authenticator.class.php @@ -62,24 +62,26 @@ class Authenticator /** * @param int $userId El Id de usuario * @param string $userLogin El login de usuario + * @param string $IV * @throws \InvalidArgumentException */ - public function __construct($userId, $userLogin = null) + public function __construct($userId, $userLogin = null, $IV = null) { $this->userId = $userId; $this->userLogin = $userLogin; - $this->initializationKey = $this->genUserInitializationKey(); + $this->initializationKey = $this->genUserInitializationKey($IV); } /** * Generar una clave de inicialización codificada en Base32 * + * @param string $IV * @return string * @throws \InvalidArgumentException */ - private function genUserInitializationKey() + private function genUserInitializationKey($IV = null) { - $userIV = UserPass::getUserIVById($this->userId); + $userIV = $IV === null ? UserPass::getUserIVById($this->userId) : $IV; $base32 = new Base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', false, true, true); return substr($base32->encode($userIV), 0, 16); diff --git a/inc/Plugins/Authenticator/AuthenticatorData.class.php b/inc/Plugins/Authenticator/AuthenticatorData.class.php index 288d439a..43847c1e 100644 --- a/inc/Plugins/Authenticator/AuthenticatorData.class.php +++ b/inc/Plugins/Authenticator/AuthenticatorData.class.php @@ -55,6 +55,12 @@ class AuthenticatorData * @var int */ public $expireDays; + /** + * Vector de inicialización + * + * @var string + */ + public $IV; /** * @return mixed @@ -119,4 +125,20 @@ class AuthenticatorData { $this->expireDays = $expireDays; } + + /** + * @return string + */ + public function getIV() + { + return $this->IV; + } + + /** + * @param string $IV + */ + public function setIV($IV) + { + $this->IV = $IV; + } } \ No newline at end of file diff --git a/inc/Plugins/Authenticator/AuthenticatorPlugin.class.php b/inc/Plugins/Authenticator/AuthenticatorPlugin.class.php index e10dfdb4..cba0bbde 100644 --- a/inc/Plugins/Authenticator/AuthenticatorPlugin.class.php +++ b/inc/Plugins/Authenticator/AuthenticatorPlugin.class.php @@ -35,6 +35,8 @@ use SplSubject; */ class AuthenticatorPlugin extends PluginBase { + const PLUGIN_NAME = 'Authenticator'; + /** * Receive update from subject * @@ -66,7 +68,7 @@ class AuthenticatorPlugin extends PluginBase * Evento de actualización * * @param string $event Nombre del evento - * @param mixed $object + * @param mixed $object */ public function updateEvent($event, $object) { @@ -153,6 +155,14 @@ class AuthenticatorPlugin extends PluginBase */ public function getName() { - return 'Authenticator'; + return self::PLUGIN_NAME; + } + + /** + * @return array|AuthenticatorData[] + */ + public function getData() + { + return (array)parent::getData(); } } \ No newline at end of file diff --git a/inc/Plugins/Authenticator/LoginController.class.php b/inc/Plugins/Authenticator/LoginController.class.php index afe4ab11..ac5164f1 100644 --- a/inc/Plugins/Authenticator/LoginController.class.php +++ b/inc/Plugins/Authenticator/LoginController.class.php @@ -124,6 +124,7 @@ class LoginController * Comprobar la caducidad del código * * @throws \SP\Core\Exceptions\SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function checkExpireTime() { @@ -139,6 +140,7 @@ class LoginController $NoticeData = new NoticeData(); $NoticeData->setNoticeComponent($this->Plugin->getName()); $NoticeData->setNoticeUserId($userId); + $NoticeData->setNoticeType(_('Aviso Caducidad')); if (count(Notice::getItem($NoticeData)->getByUserCurrentDate()) > 0){ return; @@ -148,9 +150,11 @@ class LoginController $timeRemaining = $expireTime - time(); if ($timeRemaining <= self::WARNING_TIME) { - $NoticeData->setNoticeType(_('Aviso Caducidad')); $NoticeData->setNoticeDescription(sprintf(_('El código 2FA se ha de restablecer en %d días'), $timeRemaining / 86400)); - $NoticeData->setNoticeUserId($userId); + + Notice::getItem($NoticeData)->add(); + } elseif (time() > $expireTime) { + $NoticeData->setNoticeDescription(_('El código 2FA ha caducado. Es necesario restablecerlo desde las preferencias')); Notice::getItem($NoticeData)->add(); } diff --git a/inc/Plugins/Authenticator/PreferencesController.class.php b/inc/Plugins/Authenticator/PreferencesController.class.php index 4b3e68a3..bdc8deed 100644 --- a/inc/Plugins/Authenticator/PreferencesController.class.php +++ b/inc/Plugins/Authenticator/PreferencesController.class.php @@ -26,6 +26,7 @@ namespace Plugins\Authenticator; use InvalidArgumentException; use SP\Controller\TabControllerBase; +use SP\Core\Crypt; use SP\Core\Plugin\PluginBase; use SP\Core\Plugin\PluginInterface; use SP\Util\ArrayUtil; @@ -65,20 +66,28 @@ class PreferencesController { $base = $this->Plugin->getThemeDir() . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'userpreferences'; - // Datos del plugin - $pluginData = $this->Plugin->getData() ?: []; - // Datos del usuario de la sesión $UserData = $this->Controller->getUserData(); // Buscar al usuario en los datos del plugin /** @var AuthenticatorData $AuthenticatorData */ - $AuthenticatorData = ArrayUtil::searchInObject($pluginData, 'userId', $UserData->getUserId(), new AuthenticatorData()); + $AuthenticatorData = ArrayUtil::searchInObject($this->Plugin->getData(), 'userId', $UserData->getUserId(), new AuthenticatorData()); $this->Controller->view->addTemplate('preferences-security', $base); try { - $twoFa = new Authenticator($UserData->getUserId(), $UserData->getUserLogin()); + $IV = null; + + if (!$AuthenticatorData->isTwofaEnabled()) { + $IV = Crypt::getIV(); + $AuthenticatorData->setIV($IV); + + Session::setUserData($AuthenticatorData); + } else { + $IV = $AuthenticatorData->getIV(); + } + + $twoFa = new Authenticator($UserData->getUserId(), $UserData->getUserLogin(), $IV); $this->Controller->view->assign('qrCode', !$AuthenticatorData->isTwofaEnabled() ? $twoFa->getUserQRCode() : ''); $this->Controller->view->assign('userId', $UserData->getUserId()); diff --git a/inc/Plugins/Authenticator/Session.class.php b/inc/Plugins/Authenticator/Session.class.php index 4e021cfc..a56d18c2 100644 --- a/inc/Plugins/Authenticator/Session.class.php +++ b/inc/Plugins/Authenticator/Session.class.php @@ -24,7 +24,7 @@ class Session */ public static function setTwoFApass($pass) { - CoreSession::setSessionKey('Authenticator.twofapass', $pass); + CoreSession::setPluginKey(AuthenticatorPlugin::PLUGIN_NAME, 'twofapass', $pass); } /** @@ -34,6 +34,26 @@ class Session */ public static function getTwoFApass() { - CoreSession::getSessionKey('Authenticator.twofapass'); + return CoreSession::getPluginKey(AuthenticatorPlugin::PLUGIN_NAME, 'twofapass'); + } + + /** + * Establecer los datos del usuario + * + * @param AuthenticatorData $data + */ + public static function setUserData(AuthenticatorData $data) + { + CoreSession::setPluginKey(AuthenticatorPlugin::PLUGIN_NAME, 'userdata', $data); + } + + /** + * Devolver los datos del usuario + * + * @return AuthenticatorData + */ + public static function getUserData() + { + return CoreSession::getPluginKey(AuthenticatorPlugin::PLUGIN_NAME, 'userdata'); } } \ No newline at end of file diff --git a/inc/SP/Core/Crypt.class.php b/inc/SP/Core/Crypt.class.php index 6868dc94..5e41118e 100644 --- a/inc/SP/Core/Crypt.class.php +++ b/inc/SP/Core/Crypt.class.php @@ -71,7 +71,7 @@ class Crypt * * @return string con el IV */ - private static function getIV() + public static function getIV() { $source = MCRYPT_DEV_URANDOM; $mcryptRes = self::getMcryptResource(); diff --git a/inc/SP/Core/Session.class.php b/inc/SP/Core/Session.class.php index 1ac2f27a..a527a34d 100644 --- a/inc/SP/Core/Session.class.php +++ b/inc/SP/Core/Session.class.php @@ -58,14 +58,26 @@ class Session /** * Establecer una variable de sesión * - * @param mixed $key El nombre de la variable - * @param mixed $value El valor de la variable + * @param string $key El nombre de la variable + * @param mixed $value El valor de la variable */ public static function setSessionKey($key, $value) { $_SESSION[$key] = $value; } + /** + * Establecer una variable de sesión para un plugin + * + * @param string $plugin Nombre del plugin + * @param string $key El nombre de la variable + * @param mixed $value El valor de la variable + */ + public static function setPluginKey($plugin, $key, $value) + { + $_SESSION[$plugin][$key] = $value; + } + /** * Devuelve los datos del usuario en la sesión. * @@ -79,17 +91,31 @@ class Session /** * Devolver una variable de sesión * - * @param mixed $key - * @param mixed $default + * @param string $key + * @param mixed $default * @return mixed */ public static function getSessionKey($key, $default = '') { if (isset($_SESSION[$key])) { - if (is_numeric($default)) { - return (int)$_SESSION[$key]; - } - return $_SESSION[$key]; + return is_numeric($default) ? (int)$_SESSION[$key] : $_SESSION[$key]; + } + + return $default; + } + + /** + * Devolver una variable de sesión + * + * @param string $plugin + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function getPluginKey($plugin, $key, $default = '') + { + if (isset($_SESSION[$plugin][$key])) { + return is_numeric($default) ? (int)$_SESSION[$plugin][$key] : $_SESSION[$plugin][$key]; } return $default; diff --git a/inc/SP/Http/Request.class.php b/inc/SP/Http/Request.class.php index c0c36626..135fffae 100644 --- a/inc/SP/Http/Request.class.php +++ b/inc/SP/Http/Request.class.php @@ -94,13 +94,13 @@ class Request switch ($_SERVER['REQUEST_METHOD']) { case 'GET': if (!isset($_GET[$param])) { - return ($force) ? !$force : $default; + return $force ? !$force : $default; } $value = &$_GET[$param]; break; case 'POST': if (!isset($_POST[$param])) { - return ($force) ? !$force : $default; + return $force ? !$force : $default; } $value = &$_POST[$param]; break; diff --git a/inc/SP/Util/ArrayUtil.class.php b/inc/SP/Util/ArrayUtil.class.php index 4c297382..8e381de9 100644 --- a/inc/SP/Util/ArrayUtil.class.php +++ b/inc/SP/Util/ArrayUtil.class.php @@ -41,7 +41,7 @@ class ArrayUtil * @param object $default Valor por defecto * @return false|object */ - public static function searchInObject(array &$array, $property, $value, $default = null) + public static function searchInObject(array $array, $property, $value, $default = null) { foreach($array as $object) { if ($value == $object->$property) {