mirror of
https://github.com/nuxsmin/sysPass.git
synced 2026-03-04 15:44:07 +01:00
* [DEV] Improved Authenticator plugin key management
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user