* [DEV] Improved Authenticator plugin key management

This commit is contained in:
nuxsmin
2017-01-08 11:48:48 +01:00
parent cdd89482c1
commit f713b9895e
11 changed files with 140 additions and 38 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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());

View File

@@ -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');
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {