diff --git a/inc/Base.php b/inc/Base.php
index 89fe7003..511a5ea6 100644
--- a/inc/Base.php
+++ b/inc/Base.php
@@ -81,7 +81,11 @@ function debugLog($data, $printLastCaller = false)
for ($i = 1; $i <= $n - 1; $i++) {
$class = isset($backtrace[$i]['class']) ? $backtrace[$i]['class'] : '';
- error_log(sprintf('Caller %d: %s\%s', $i, $class, $backtrace[$i]['function']));
+ $line = sprintf('Caller %d: %s\%s', $i, $class, $backtrace[$i]['function']);
+
+ if (!error_log($line . PHP_EOL, 3, LOG_FILE)) {
+ error_log($line);
+ }
}
}
}
diff --git a/inc/SP/Api/ApiBase.class.php b/inc/SP/Api/ApiBase.class.php
index 113de794..40da4e0e 100644
--- a/inc/SP/Api/ApiBase.class.php
+++ b/inc/SP/Api/ApiBase.class.php
@@ -2,8 +2,8 @@
/**
* sysPass
*
- * @author nuxsmin
- * @link http://syspass.org
+ * @author nuxsmin
+ * @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
@@ -26,18 +26,18 @@ namespace SP\Api;
defined('APP_ROOT') || die();
-use SP\Auth\Auth;
-use SP\Auth\AuthResult;
-use SP\Auth\AuthUtil;
+use Defuse\Crypto\Exception\CryptoException;
+use SP\Core\Crypt\Crypt;
+use SP\Core\Crypt\Hash;
use SP\Core\Exceptions\InvalidArgumentException;
use SP\Core\Exceptions\SPException;
use SP\Core\Session;
use SP\Core\SessionUtil;
-use SP\DataModel\UserData;
+use SP\DataModel\ApiTokenData;
use SP\DataModel\UserLoginData;
use SP\Log\Log;
+use SP\Mgmt\ApiTokens\ApiToken;
use SP\Mgmt\Users\User;
-use SP\Mgmt\Users\UserPass;
use SP\Util\Json;
/**
@@ -71,10 +71,6 @@ abstract class ApiBase implements ApiInterface
* @var mixed
*/
protected $data;
- /**
- * @var string
- */
- protected $mPass = '';
/**
* @var UserLoginData
*/
@@ -83,6 +79,10 @@ abstract class ApiBase implements ApiInterface
* @var Log
*/
protected $Log;
+ /**
+ * @var ApiTokenData
+ */
+ protected $ApiTokenData;
/**
* @param $data
@@ -91,18 +91,19 @@ abstract class ApiBase implements ApiInterface
public function __construct($data)
{
$this->actionId = $this->getActionId($data->method);
+ $this->ApiTokenData = ApiToken::getItem()->getTokenByToken($this->actionId, $data->params->authToken);
- if (!AuthUtil::checkAuthToken($this->actionId, $data->params->authToken)) {
+ if ($this->ApiTokenData === false) {
throw new SPException(SPException::SP_CRITICAL, __('Acceso no permitido', false));
}
$this->data = $data;
- $this->userId = ApiTokensUtil::getUserIdForToken($data->params->authToken);
+ $this->userId = $this->ApiTokenData->getAuthtokenUserId();
$this->loadUserData();
- if ($this->getParam('userPass') !== null) {
+ if ($this->getParam('pass') !== null) {
$this->doAuth();
}
@@ -127,17 +128,11 @@ abstract class ApiBase implements ApiInterface
/**
* Cargar los datos del usuario
*
- * @return UserData
- * @throws \SP\Core\Exceptions\InvalidClassException
* @throws \SP\Core\Exceptions\SPException
*/
protected function loadUserData()
{
- $UserData = new UserData();
- $UserData->setUserId($this->userId);
- $UserData->setUserPass($this->getParam('userPass'));
-
- $this->UserData = User::getItem($UserData)->getById($this->userId);
+ $this->UserData = User::getItem()->getById($this->ApiTokenData->getAuthtokenUserId());
SessionUtil::loadUserSession($this->UserData);
}
@@ -145,9 +140,9 @@ abstract class ApiBase implements ApiInterface
/**
* Devolver el valor de un parámetro
*
- * @param string $name Nombre del parámetro
- * @param bool $required Si es requerido
- * @param mixed $default Valor por defecto
+ * @param string $name Nombre del parámetro
+ * @param bool $required Si es requerido
+ * @param mixed $default Valor por defecto
* @return int|string
* @throws SPException
*/
@@ -168,36 +163,30 @@ abstract class ApiBase implements ApiInterface
* Realizar la autentificación del usuario
*
* @throws SPException
- * @throws \SP\Core\Exceptions\InvalidClassException
- * @throws \Defuse\Crypto\Exception\BadFormatException
- * @throws \Defuse\Crypto\Exception\CryptoException
- * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
*/
protected function doAuth()
{
- $Auth = new Auth($this->UserData);
- $resAuth = $Auth->doAuth();
-
- if ($resAuth !== false) {
- /** @var AuthResult $AuthResult */
- foreach ($resAuth as $AuthResult) {
- $data = $AuthResult->getData();
-
- if ($data->getAuthenticated() && $data->getStatusCode() === 0) {
- break;
- }
- }
- } else {
+ if ($this->UserData->isUserIsDisabled()
+ || !Hash::checkHashKey($this->getParam('pass'), $this->ApiTokenData->getAuthtokenHash())
+ ) {
throw new SPException(SPException::SP_CRITICAL, __('Acceso no permitido', false));
}
+ }
- if (!$this->UserData->isUserIsDisabled()
- && UserPass::loadUserMPass($this->UserData) === UserPass::MPASS_OK
- ) {
- $this->auth = true;
- $this->mPass = UserPass::getClearUserMPass();
- } else {
- throw new SPException(SPException::SP_CRITICAL, __('Acceso no permitido', false));
+ /**
+ * Devolver la clave maestra
+ *
+ * @return string
+ * @throws SPException
+ */
+ protected function getMPass()
+ {
+ try {
+ $key = $this->getParam('pass') . $this->getParam('authToken');
+ $securedKey = Crypt::unlockSecuredKey($this->ApiTokenData->getAuthtokenKey(), $key);
+ return Crypt::decrypt($this->ApiTokenData->getAuthtokenPass(), $securedKey, $key);
+ } catch (CryptoException $e) {
+ throw new SPException(SPException::SP_ERROR, __('Error interno', false), $e->getMessage());
}
}
diff --git a/inc/SP/Api/ApiTokens.class.php b/inc/SP/Api/ApiTokens.class.php
deleted file mode 100644
index bf06a0e1..00000000
--- a/inc/SP/Api/ApiTokens.class.php
+++ /dev/null
@@ -1,314 +0,0 @@
-.
- */
-
-namespace SP\Api;
-
-defined('APP_ROOT') || die();
-
-use SP\Core\Exceptions\SPException;
-use SP\Core\Session;
-use SP\Storage\DB;
-use SP\Storage\QueryData;
-
-/**
- * Class ApiTokens para la gestión de autorizaciones de acceso a la API de sysPass
- *
- * @package SP
- */
-class ApiTokens
-{
- /**
- * @var int
- */
- private $tokenId = 0;
- /**
- * @var int
- */
- private $userId = 0;
- /**
- * @var int
- */
- private $actionId = 0;
- /**
- * @var string
- */
- private $token = '';
- /**
- * @var bool
- */
- private $refreshToken = false;
-
- /**
- * @param boolean $refreshToken
- */
- public function setRefreshToken($refreshToken)
- {
- $this->refreshToken = $refreshToken;
- }
-
- /**
- * Añadir un nuevo token
- *
- * @throws SPException
- */
- public function addToken()
- {
- $this->checkTokenExist();
-
- if ($this->refreshToken) {
- $this->refreshToken();
- }
-
- $query = /** @lang SQL */
- 'INSERT INTO authTokens
- SET authtoken_userId = :userid,
- authtoken_actionId = :actionid,
- authtoken_createdBy = :createdby,
- authtoken_token = :token,
- authtoken_startDate = UNIX_TIMESTAMP()';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($this->userId, 'userid');
- $Data->addParam($this->actionId, 'actionid');
- $Data->addParam(Session::getUserData()->getUserId(), 'createdby');
- $Data->addParam($this->getUserToken() ? $this->token : $this->generateToken(), 'token');
- $Data->setOnErrorMessage(__('Error interno', false));
-
- DB::getQuery($Data);
- }
-
- /**
- * Comprobar si el token ya existe
- *
- * @return bool
- * @throws SPException
- */
- private function checkTokenExist()
- {
- $query = /** @lang SQL */
- 'SELECT authtoken_id FROM authTokens
- WHERE authtoken_userId = :userid
- AND authtoken_actionId = :actionid
- AND authtoken_id <> :id LIMIT 1';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($this->tokenId, 'id');
- $Data->addParam($this->userId, 'userid');
- $Data->addParam($this->actionId, 'actionid');
-
- try {
- DB::getResults($Data);
- } catch (SPException $e) {
- throw new SPException(SPException::SP_CRITICAL, __('Error interno', false));
- }
-
- if ($Data->getQueryNumRows() === 1) {
- throw new SPException(SPException::SP_WARNING, __('La autorización ya existe', false));
- }
- }
-
- /**
- * Regenerar el hash de los tokens de un usuario
- *
- * @throws SPException
- */
- private function refreshToken()
- {
- $query = /** @lang SQL */
- 'UPDATE authTokens SET
- authtoken_token = :token,
- authtoken_startDate = UNIX_TIMESTAMP()
- WHERE authtoken_userId = :userid';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($this->userId, 'userid');
- $Data->addParam($this->generateToken(), 'token');
- $Data->setOnErrorMessage(__('Error interno', false));
-
- DB::getQuery($Data);
- }
-
- /**
- * Generar un token de acceso
- *
- * @return string
- */
- private function generateToken()
- {
- return sha1(uniqid('sysPass-API', true) . time());
- }
-
- /**
- * Obtener el token de la API de un usuario
- *
- * @return bool
- * @throws SPException
- */
- private function getUserToken()
- {
- $query = /** @lang SQL */
- 'SELECT authtoken_token FROM authTokens WHERE authtoken_userId = :userid LIMIT 1';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($this->userId, 'userid');
-
- try {
- $queryRes = DB::getResults($Data);
- } catch (SPException $e) {
- throw new SPException(SPException::SP_CRITICAL, __('Error interno', false));
- }
-
- if ($Data->getQueryNumRows() === 0) {
- return false;
- }
-
- $this->token = $queryRes->authtoken_token;
-
- return true;
- }
-
- /**
- * Actualizar un token
- *
- * @throws \SP\Core\Exceptions\SPException
- */
- public function updateToken()
- {
- $this->checkTokenExist();
-
- if ($this->refreshToken) {
- $this->refreshToken();
- }
-
- $query = /** @lang SQL */
- 'UPDATE authTokens
- SET authtoken_userId = :userid,
- authtoken_actionId = :actionid,
- authtoken_createdBy = :createdby,
- authtoken_token = :token,
- authtoken_startDate = UNIX_TIMESTAMP()
- WHERE authtoken_id = :id LIMIT 1';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($this->tokenId, 'id');
- $Data->addParam($this->userId, 'userid');
- $Data->addParam($this->actionId, 'actionid');
- $Data->addParam(Session::getUserData()->getUserId(), 'createdby');
- $Data->addParam($this->getUserToken() ? $this->token : $this->generateToken(), 'token');
- $Data->setOnErrorMessage(__('Error interno', false));
-
- DB::getQuery($Data);
- }
-
- /**
- * Eliminar token
- *
- * @param $id
- */
- public function deleteToken($id)
- {
- $query = /** @lang SQL */
- 'DELETE FROM authTokens WHERE authtoken_id = ? LIMIT 1';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($id);
- $Data->setOnErrorMessage(__('Error interno', false));
-
- DB::getQuery($Data);
- }
-
-
- /**
- * Eliminar token
- *
- * @param array $ids
- * @throws \SP\Core\Exceptions\ConstraintException
- */
- public function deleteTokenBatch(array $ids)
- {
- $query = /** @lang SQL */
- 'DELETE FROM authTokens WHERE authtoken_id IN (' . implode(',', array_fill(0, count($ids), '?')) . ')';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->setParams($ids);
- $Data->setOnErrorMessage(__('Error interno', false));
-
- DB::getQuery($Data);
- }
-
- /**
- * @return int
- */
- public function getUserId()
- {
- return $this->userId;
- }
-
- /**
- * @param int $userId
- */
- public function setUserId($userId)
- {
- $this->userId = $userId;
- }
-
- /**
- * @return int
- */
- public function getTokenId()
- {
- return $this->tokenId;
- }
-
- /**
- * @param int $tokenId
- */
- public function setTokenId($tokenId)
- {
- $this->tokenId = $tokenId;
- }
-
- /**
- * @return int
- */
- public function getActionId()
- {
- return $this->actionId;
- }
-
- /**
- * @param int $actionId
- */
- public function setActionId($actionId)
- {
- $this->actionId = $actionId;
- }
-}
\ No newline at end of file
diff --git a/inc/SP/Api/ApiTokensUtil.class.php b/inc/SP/Api/ApiTokensUtil.class.php
index 724735fb..b00bfbe4 100644
--- a/inc/SP/Api/ApiTokensUtil.class.php
+++ b/inc/SP/Api/ApiTokensUtil.class.php
@@ -2,8 +2,8 @@
/**
* sysPass
*
- * @author nuxsmin
- * @link http://syspass.org
+ * @author nuxsmin
+ * @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
@@ -26,10 +26,6 @@ namespace SP\Api;
use SP\Core\Acl;
use SP\Core\ActionsInterface;
-use SP\Core\Exceptions\SPException;
-use SP\DataModel\ItemSearchData;
-use SP\Storage\DB;
-use SP\Storage\QueryData;
defined('APP_ROOT') || die();
@@ -40,92 +36,6 @@ defined('APP_ROOT') || die();
*/
class ApiTokensUtil
{
- /**
- * Obtener los tokens de la API
- *
- * @param int $tokenId opcional, con el Id del token a consultar
- * @param bool $returnRawData Devolver la consulta tal cual
- * @return array|object con la lista de tokens
- */
- public static function getTokens($tokenId = null, $returnRawData = false)
- {
- $query = 'SELECT authtoken_id,' .
- 'authtoken_userId,' .
- 'authtoken_actionId, ' .
- 'authtoken_token, ' .
- 'user_login ' .
- 'FROM authTokens ' .
- 'LEFT JOIN usrData ON user_id = authtoken_userId ';
-
- $Data = new QueryData();
-
- if (null !== $tokenId) {
- $query .= 'WHERE authtoken_id = ? LIMIT 1';
- $Data->addParam($tokenId);
- } else {
- $query .= 'ORDER BY user_login';
- }
-
- $Data->setQuery($query);
-
- if (!$returnRawData) {
- $queryRes = DB::getResultsArray($Data);
-
- foreach ($queryRes as &$token) {
- $token->authtoken_actionId = Acl::getActionName($token->authtoken_actionId);
- }
- } else {
- $queryRes = DB::getResults($Data);
- }
-
- return $queryRes;
- }
-
- /**
- * Obtener los tokens de la API de una búsqueda
- * @param ItemSearchData $SearchData
- * @return array|object con la lista de tokens
- */
- public static function getTokensMgmtSearch(ItemSearchData $SearchData)
- {
- $query = 'SELECT authtoken_id,' .
- 'authtoken_userId,' .
- 'authtoken_actionId, ' .
- 'authtoken_token, ' .
- 'user_login ' .
- 'FROM authTokens ' .
- 'LEFT JOIN usrData ON user_id = authtoken_userId ';
-
- $Data = new QueryData();
-
- if ($SearchData->getSeachString() !== '') {
- $search = '%' . $SearchData->getSeachString() . '%';
- $query .= ' WHERE user_login LIKE ?';
-
- $Data->addParam($search);
- }
-
- $query .= ' ORDER BY user_login';
- $query .= ' LIMIT ?, ?';
-
- $Data->addParam($SearchData->getLimitStart());
- $Data->addParam($SearchData->getLimitCount());
-
- $Data->setQuery($query);
-
- DB::setFullRowCount();
-
- $queryRes = DB::getResultsArray($Data);
-
- foreach ($queryRes as &$token) {
- $token->authtoken_actionId = Acl::getActionName($token->authtoken_actionId);
- }
-
- $queryRes['count'] = $Data->getQueryNumRows();
-
- return $queryRes;
- }
-
/**
* Devuelver un array de acciones posibles para los tokens
*
@@ -146,32 +56,4 @@ class ApiTokensUtil
return $actions;
}
-
- /**
- * Obtener el usuario a partir del token
- *
- * @param $token string El token de autorización
- * @return bool|mixed
- * @throws \SP\Core\Exceptions\SPException
- */
- public static function getUserIdForToken($token)
- {
- $query = 'SELECT authtoken_userId FROM authTokens WHERE authtoken_token = ? LIMIT 1';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($token);
-
- try {
- $queryRes = DB::getResults($Data);
- } catch (SPException $e) {
- throw new SPException(SPException::SP_CRITICAL, __('Error interno', false));
- }
-
- if ($Data->getQueryNumRows() === 0) {
- return false;
- }
-
- return $queryRes->authtoken_userId;
- }
}
\ No newline at end of file
diff --git a/inc/SP/Api/SyspassApi.class.php b/inc/SP/Api/SyspassApi.class.php
index 619a90c8..76f17579 100644
--- a/inc/SP/Api/SyspassApi.class.php
+++ b/inc/SP/Api/SyspassApi.class.php
@@ -88,11 +88,12 @@ class SyspassApi extends ApiBase
$LogMessage->addDetails(__('Origen', false), 'API');
$this->Log->writeLog();
- $securedKey = Crypt::unlockSecuredKey($AccountData->getAccountKey(), $this->mPass);
+ $mPass = $this->getMPass();
+ $securedKey = Crypt::unlockSecuredKey($AccountData->getAccountKey(), $mPass);
$ret = [
'itemId' => $accountId,
- 'pass' => Crypt::decrypt($AccountData->getAccountPass(), $securedKey)
+ 'pass' => Crypt::decrypt($AccountData->getAccountPass(), $securedKey, $mPass)
];
if ($this->getParam('details', false, 0)) {
@@ -158,6 +159,11 @@ class SyspassApi extends ApiBase
* Añadir una nueva cuenta
*
* @return string La cadena en formato JSON
+ * @throws \SP\Core\Exceptions\QueryException
+ * @throws \SP\Core\Exceptions\ConstraintException
+ * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
+ * @throws \Defuse\Crypto\Exception\CryptoException
+ * @throws \Defuse\Crypto\Exception\BadFormatException
* @throws \SP\Core\Exceptions\SPException
*/
public function addAccount()
diff --git a/inc/SP/Auth/AuthUtil.class.php b/inc/SP/Auth/AuthUtil.class.php
index e44237ba..56f67dbb 100644
--- a/inc/SP/Auth/AuthUtil.class.php
+++ b/inc/SP/Auth/AuthUtil.class.php
@@ -31,8 +31,6 @@ use SP\DataModel\UserPassRecoverData;
use SP\Html\Html;
use SP\Log\Email;
use SP\Mgmt\Users\UserPassRecover;
-use SP\Storage\DB;
-use SP\Storage\QueryData;
use SP\Util\Util;
/**
@@ -79,32 +77,6 @@ class AuthUtil
return false;
}
- /**
- * Comprobar el token de seguridad
- *
- * @param $actionId int El id de la accion
- * @param $token string El token de seguridad
- * @return bool
- * @throws \SP\Core\Exceptions\SPException
- */
- public static function checkAuthToken($actionId, $token)
- {
- $query = /** @lang SQL */
- 'SELECT authtoken_id
- FROM authTokens
- WHERE authtoken_actionId = ?
- AND authtoken_token = ? LIMIT 1';
-
- $Data = new QueryData();
- $Data->setQuery($query);
- $Data->addParam($actionId);
- $Data->addParam($token);
-
- DB::getQuery($Data);
-
- return $Data->getQueryNumRows() === 1;
- }
-
/**
* Devuelve el typo de autentificación del servidor web
*
diff --git a/inc/SP/Controller/ItemActionController.class.php b/inc/SP/Controller/ItemActionController.class.php
index 0785394d..608b482c 100644
--- a/inc/SP/Controller/ItemActionController.class.php
+++ b/inc/SP/Controller/ItemActionController.class.php
@@ -29,7 +29,6 @@ use SP\Account\AccountFavorites;
use SP\Account\AccountHistory;
use SP\Account\AccountHistoryUtil;
use SP\Account\AccountUtil;
-use SP\Api\ApiTokens;
use SP\Auth\AuthUtil;
use SP\Core\ActionsInterface;
use SP\Core\Messages\LogMessage;
@@ -51,6 +50,7 @@ use SP\Forms\UserForm;
use SP\Http\Request;
use SP\Log\Email;
use SP\Log\Log;
+use SP\Mgmt\ApiTokens\ApiToken;
use SP\Mgmt\Categories\Category;
use SP\Mgmt\Customers\Customer;
use SP\Mgmt\CustomFields\CustomField;
@@ -593,37 +593,48 @@ class ItemActionController implements ItemControllerInterface
* @throws \SP\Core\Exceptions\SPException
* @throws \phpmailer\phpmailerException
* @throws \SP\Core\Exceptions\ConstraintException
+ * @throws \SP\Core\Exceptions\QueryException
*/
protected function tokenAction()
{
$Form = new ApiTokenForm($this->itemId);
+ $refresh = Request::analyze('refreshtoken', false, false, true);
+
switch ($this->actionId) {
case ActionsInterface::ACTION_MGM_APITOKENS_NEW:
$Form->validate($this->actionId);
- $Form->getItemData()->addToken();
+
+ if ($refresh === true) {
+ ApiToken::getItem($Form->getItemData())->refreshToken()->add();
+ } else {
+ ApiToken::getItem($Form->getItemData())->add();
+ }
$this->LogMessage->setAction(__('Crear Autorización', false));
$this->LogMessage->addDescription(__('Autorización creada', false));
- $this->LogMessage->addDetails(__('Usuario', false), UserUtil::getUserLoginById($Form->getItemData()->getUserId()));
+ $this->LogMessage->addDetails(__('Usuario', false), UserUtil::getUserLoginById($Form->getItemData()->getAuthtokenUserId()));
break;
case ActionsInterface::ACTION_MGM_APITOKENS_EDIT:
$Form->validate($this->actionId);
- $Form->getItemData()->updateToken();
+
+ if ($refresh === true) {
+ ApiToken::getItem($Form->getItemData())->refreshToken()->update();
+ } else {
+ ApiToken::getItem($Form->getItemData())->update();
+ }
$this->LogMessage->setAction(__('Actualizar Autorización', false));
$this->LogMessage->addDescription(__('Autorización actualizada', false));
- $this->LogMessage->addDetails(__('Usuario', false), UserUtil::getUserLoginById($Form->getItemData()->getUserId()));
+ $this->LogMessage->addDetails(__('Usuario', false), UserUtil::getUserLoginById($Form->getItemData()->getAuthtokenUserId()));
break;
case ActionsInterface::ACTION_MGM_APITOKENS_DELETE:
- $ApiToken = new ApiTokens();
-
if (is_array($this->itemId)) {
- $ApiToken->deleteTokenBatch($this->itemId);
+ ApiToken::getItem()->deleteBatch($this->itemId);
$this->LogMessage->addDescription(__('Autorizaciones eliminadas', false));
} else {
- $ApiToken->deleteToken($this->itemId);
+ ApiToken::getItem()->delete($this->itemId);
$this->LogMessage->addDescription(__('Autorización eliminada', false));
}
diff --git a/inc/SP/Controller/ItemListController.class.php b/inc/SP/Controller/ItemListController.class.php
index c9e0c6fb..94451963 100644
--- a/inc/SP/Controller/ItemListController.class.php
+++ b/inc/SP/Controller/ItemListController.class.php
@@ -36,6 +36,8 @@ use SP\Core\Exceptions\SPException;
use SP\Core\Template;
use SP\DataModel\ItemSearchData;
use SP\Http\Request;
+use SP\Mgmt\ApiTokens\ApiToken;
+use SP\Mgmt\ApiTokens\ApiTokenSearch;
use SP\Mgmt\Categories\CategorySearch;
use SP\Mgmt\Customers\CustomerSearch;
use SP\Mgmt\CustomFields\CustomFieldDefSearch;
@@ -377,7 +379,7 @@ class ItemListController extends GridTabControllerBase implements ActionsInterfa
}
$Grid = $this->getGrids()->getTokensGrid();
- $Grid->getData()->setData(ApiTokensUtil::getTokensMgmtSearch($this->ItemSearchData));
+ $Grid->getData()->setData(ApiTokenSearch::getItem()->getMgmtSearch($this->ItemSearchData));
$Grid->updatePager();
$this->view->append('tabs', $Grid);
diff --git a/inc/SP/Controller/ItemSearchController.class.php b/inc/SP/Controller/ItemSearchController.class.php
index 979c68f4..22c25f93 100644
--- a/inc/SP/Controller/ItemSearchController.class.php
+++ b/inc/SP/Controller/ItemSearchController.class.php
@@ -36,6 +36,7 @@ use SP\Core\SessionUtil;
use SP\Core\Template;
use SP\DataModel\ItemSearchData;
use SP\Http\Request;
+use SP\Mgmt\ApiTokens\ApiTokenSearch;
use SP\Mgmt\Categories\CategorySearch;
use SP\Mgmt\Customers\CustomerSearch;
use SP\Mgmt\CustomFields\CustomFieldDefSearch;
@@ -248,6 +249,7 @@ class ItemSearchController extends GridItemsSearchController implements ActionsI
* Obtener los tokens API de una búsqueda
*
* @throws \InvalidArgumentException
+ * @throws \SP\Core\Exceptions\InvalidArgumentException
*/
public function getTokens()
{
@@ -260,7 +262,7 @@ class ItemSearchController extends GridItemsSearchController implements ActionsI
$this->view->addTemplate('datagrid-table', 'grid');
$Grid = $this->getGrids()->getTokensGrid();
- $Grid->getData()->setData(ApiTokensUtil::getTokensMgmtSearch($this->ItemSearchData));
+ $Grid->getData()->setData(ApiTokenSearch::getItem()->getMgmtSearch($this->ItemSearchData));
$Grid->updatePager();
$this->updatePager($Grid->getPager(), $this->ItemSearchData);
diff --git a/inc/SP/Controller/ItemShowController.class.php b/inc/SP/Controller/ItemShowController.class.php
index 182a6a18..fe373626 100644
--- a/inc/SP/Controller/ItemShowController.class.php
+++ b/inc/SP/Controller/ItemShowController.class.php
@@ -39,6 +39,7 @@ use SP\Core\Session;
use SP\Core\SessionUtil;
use SP\Core\Template;
use SP\DataModel\AccountExtData;
+use SP\DataModel\ApiTokenData;
use SP\DataModel\CategoryData;
use SP\DataModel\CustomerData;
use SP\DataModel\CustomFieldData;
@@ -51,6 +52,7 @@ use SP\DataModel\UserPassData;
use SP\Http\Request;
use SP\Log\Email;
use SP\Log\Log;
+use SP\Mgmt\ApiTokens\ApiToken;
use SP\Mgmt\Categories\Category;
use SP\Mgmt\Customers\Customer;
use SP\Mgmt\CustomFields\CustomField;
@@ -66,6 +68,7 @@ use SP\Mgmt\PublicLinks\PublicLink;
use SP\Mgmt\Tags\Tag;
use SP\Mgmt\Users\User;
use SP\Mgmt\Users\UserPass;
+use SP\Mgmt\Users\UserUtil;
use SP\Util\Checks;
use SP\Util\ImageUtil;
use SP\Util\Json;
@@ -392,18 +395,19 @@ class ItemShowController extends ControllerBase implements ActionsInterface, Ite
$this->module = self::ACTION_MGM_APITOKENS;
$this->view->addTemplate('tokens');
- $token = ApiTokensUtil::getTokens($this->itemId, true);
+ $ApiTokenData = $this->itemId ? ApiToken::getItem()->getById($this->itemId) : new ApiTokenData();
$this->view->assign('users', User::getItem()->getItemsForSelect());
$this->view->assign('actions', ApiTokensUtil::getTokenActions());
- $this->view->assign('token', $token);
- $this->view->assign('gotData', is_object($token));
+ $this->view->assign('ApiTokenData', $ApiTokenData);
+ $this->view->assign('isDisabled', ($this->view->actionId === self::ACTION_MGM_APITOKENS_VIEW) ? 'disabled' : '');
+ $this->view->assign('isReadonly', $this->view->isDisabled ? 'readonly' : '');
if ($this->view->isView === true) {
$Log = Log::newLog(__('Autorizaciones', false));
$LogMessage = $Log->getLogMessage();
$LogMessage->addDescription(__('Token de autorización visualizado'));
- $LogMessage->addDetails(__('Usuario'), $token->user_login);
+ $LogMessage->addDetails(__('Usuario'), UserUtil::getUserLoginById($ApiTokenData->authtoken_userId));
$Log->writeLog();
Email::sendEmail($LogMessage);
diff --git a/inc/SP/DataModel/ApiTokenData.class.php b/inc/SP/DataModel/ApiTokenData.class.php
new file mode 100644
index 00000000..413559de
--- /dev/null
+++ b/inc/SP/DataModel/ApiTokenData.class.php
@@ -0,0 +1,230 @@
+.
+ */
+
+namespace SP\DataModel;
+
+/**
+ * Class ApiTokenData
+ *
+ * @package SP\DataModel
+ */
+class ApiTokenData extends DataModelBase implements DataModelInterface
+{
+ /**
+ * @var int
+ */
+ public $authtoken_id;
+ /**
+ * @var string
+ */
+ public $authtoken_key;
+ /**
+ * @var string
+ */
+ public $authtoken_pass;
+ /**
+ * @var int
+ */
+ public $authtoken_userId;
+ /**
+ * @var string
+ */
+ public $authtoken_token = '';
+ /**
+ * @var int
+ */
+ public $authtoken_createdBy;
+ /**
+ * @var int
+ */
+ public $authtoken_startDate;
+ /**
+ * @var int
+ */
+ public $authtoken_actionId;
+ /**
+ * @var string
+ */
+ public $authtoken_hash;
+
+ /**
+ * @return int
+ */
+ public function getAuthtokenId()
+ {
+ return (int)$this->authtoken_id;
+ }
+
+ /**
+ * @param int $authtoken_id
+ */
+ public function setAuthtokenId($authtoken_id)
+ {
+ $this->authtoken_id = (int)$authtoken_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getAuthtokenKey()
+ {
+ return $this->authtoken_key;
+ }
+
+ /**
+ * @param mixed $authtoken_key
+ */
+ public function setAuthtokenKey($authtoken_key)
+ {
+ $this->authtoken_key = $authtoken_key;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthtokenUserId()
+ {
+ return (int)$this->authtoken_userId;
+ }
+
+ /**
+ * @param int $authtoken_userId
+ */
+ public function setAuthtokenUserId($authtoken_userId)
+ {
+ $this->authtoken_userId = (int)$authtoken_userId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAuthtokenToken()
+ {
+ return $this->authtoken_token;
+ }
+
+ /**
+ * @param string $authtoken_token
+ */
+ public function setAuthtokenToken($authtoken_token)
+ {
+ $this->authtoken_token = $authtoken_token;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthtokenCreatedBy()
+ {
+ return (int)$this->authtoken_createdBy;
+ }
+
+ /**
+ * @param int $authtoken_createdBy
+ */
+ public function setAuthtokenCreatedBy($authtoken_createdBy)
+ {
+ $this->authtoken_createdBy = (int)$authtoken_createdBy;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthtokenStartDate()
+ {
+ return (int)$this->authtoken_startDate;
+ }
+
+ /**
+ * @param int $authtoken_startDate
+ */
+ public function setAuthtokenStartDate($authtoken_startDate)
+ {
+ $this->authtoken_startDate = (int)$authtoken_startDate;
+ }
+
+ /**
+ * @return int
+ */
+ public function getId()
+ {
+ return (int)$this->authtoken_id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return '';
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthtokenActionId()
+ {
+ return (int)$this->authtoken_actionId;
+ }
+
+ /**
+ * @param int $authtoken_actionId
+ */
+ public function setAuthtokenActionId($authtoken_actionId)
+ {
+ $this->authtoken_actionId = (int)$authtoken_actionId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAuthtokenHash()
+ {
+ return $this->authtoken_hash;
+ }
+
+ /**
+ * @param string $authtoken_hash
+ */
+ public function setAuthtokenHash($authtoken_hash)
+ {
+ $this->authtoken_hash = $authtoken_hash;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAuthtokenPass()
+ {
+ return $this->authtoken_pass;
+ }
+
+ /**
+ * @param string $authtoken_pass
+ */
+ public function setAuthtokenPass($authtoken_pass)
+ {
+ $this->authtoken_pass = $authtoken_pass;
+ }
+}
\ No newline at end of file
diff --git a/inc/SP/Forms/ApiTokenForm.class.php b/inc/SP/Forms/ApiTokenForm.class.php
index c1393ee1..5f0fdd65 100644
--- a/inc/SP/Forms/ApiTokenForm.class.php
+++ b/inc/SP/Forms/ApiTokenForm.class.php
@@ -24,9 +24,9 @@
namespace SP\Forms;
-use SP\Api\ApiTokens;
use SP\Core\ActionsInterface;
use SP\Core\Exceptions\ValidationException;
+use SP\DataModel\ApiTokenData;
use SP\Http\Request;
/**
@@ -37,15 +37,15 @@ use SP\Http\Request;
class ApiTokenForm extends FormBase implements FormInterface
{
/**
- * @var ApiTokens
+ * @var ApiTokenData
*/
- protected $ApiTokens;
+ protected $ApiTokenData;
/**
* Validar el formulario
*
* @param $action
- * @return bool
+ * @return ApiTokenForm
* @throws \SP\Core\Exceptions\ValidationException
*/
public function validate($action)
@@ -58,7 +58,7 @@ class ApiTokenForm extends FormBase implements FormInterface
break;
}
- return true;
+ return $this;
}
/**
@@ -68,11 +68,11 @@ class ApiTokenForm extends FormBase implements FormInterface
*/
protected function analyzeRequestData()
{
- $this->ApiTokens = new ApiTokens();
- $this->ApiTokens->setTokenId($this->itemId);
- $this->ApiTokens->setUserId(Request::analyze('users', 0));
- $this->ApiTokens->setActionId(Request::analyze('actions', 0));
- $this->ApiTokens->setRefreshToken(Request::analyze('refreshtoken', false, false, true));
+ $this->ApiTokenData = new ApiTokenData();
+ $this->ApiTokenData->setAuthtokenId($this->itemId);
+ $this->ApiTokenData->setAuthtokenUserId(Request::analyze('users', 0));
+ $this->ApiTokenData->setAuthtokenActionId(Request::analyze('actions', 0));
+ $this->ApiTokenData->setAuthtokenHash(Request::analyzeEncrypted('pass'));
}
/**
@@ -80,18 +80,20 @@ class ApiTokenForm extends FormBase implements FormInterface
*/
protected function checkCommon()
{
- if ($this->ApiTokens->getUserId() === 0) {
+ if ($this->ApiTokenData->getAuthtokenUserId() === 0) {
throw new ValidationException(__('Usuario no indicado', false));
- } elseif ($this->ApiTokens->getActionId() === 0) {
+ } elseif ($this->ApiTokenData->getAuthtokenActionId() === 0) {
throw new ValidationException(__('Acción no indicada', false));
+ } elseif ($this->ApiTokenData->getAuthtokenKey() === '') {
+ throw new ValidationException(__('La clave no puede estar en blanco', false));
}
}
/**
- * @return ApiTokens
+ * @return ApiTokenData
*/
public function getItemData()
{
- return $this->ApiTokens;
+ return $this->ApiTokenData;
}
}
\ No newline at end of file
diff --git a/inc/SP/Forms/FormInterface.class.php b/inc/SP/Forms/FormInterface.class.php
index c8b36c24..6a8389ac 100644
--- a/inc/SP/Forms/FormInterface.class.php
+++ b/inc/SP/Forms/FormInterface.class.php
@@ -35,7 +35,7 @@ interface FormInterface
* Validar el formulario
*
* @param $action
- * @return bool
+ * @return FormInterface
* @throws \SP\Core\Exceptions\ValidationException
*/
public function validate($action);
diff --git a/inc/SP/Mgmt/ApiTokens/ApiToken.class.php b/inc/SP/Mgmt/ApiTokens/ApiToken.class.php
new file mode 100644
index 00000000..58f33cba
--- /dev/null
+++ b/inc/SP/Mgmt/ApiTokens/ApiToken.class.php
@@ -0,0 +1,409 @@
+.
+ */
+
+namespace SP\Mgmt\ApiTokens;
+
+use SP\Core\Crypt\Crypt;
+use SP\Core\Crypt\Hash;
+use SP\Core\Crypt\Session as CryptSession;
+use SP\Core\Exceptions\SPException;
+use SP\Core\Session;
+use SP\DataModel\ApiTokenData;
+use SP\Mgmt\ItemInterface;
+use SP\Mgmt\ItemTrait;
+use SP\Storage\DB;
+use SP\Storage\QueryData;
+use SP\Util\Util;
+
+/**
+ * Class ApiToken
+ *
+ * @package SP\Mgmt\ApiTokens
+ */
+class ApiToken extends ApiTokenBase implements ItemInterface
+{
+ use ItemTrait;
+
+ /**
+ * @return mixed
+ * @throws \Defuse\Crypto\Exception\CryptoException
+ * @throws \SP\Core\Exceptions\SPException
+ * @throws \SP\Core\Exceptions\QueryException
+ * @throws \SP\Core\Exceptions\ConstraintException
+ */
+ public function add()
+ {
+ if ($this->checkDuplicatedOnAdd()) {
+ throw new SPException(SPException::SP_WARNING, __('La autorización ya existe', false));
+ }
+
+ $token = $this->getTokenByUserId($this->itemData->getAuthtokenUserId());
+ $this->setSecureData($token);
+
+ $query = /** @lang SQL */
+ 'INSERT INTO authTokens
+ SET authtoken_userId = ?,
+ authtoken_actionId = ?,
+ authtoken_createdBy = ?,
+ authtoken_token = ?,
+ authtoken_key = ?,
+ authtoken_pass = ?,
+ authtoken_hash = ?,
+ authtoken_startDate = UNIX_TIMESTAMP()';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($this->itemData->getAuthtokenUserId());
+ $Data->addParam($this->itemData->getAuthtokenActionId());
+ $Data->addParam(Session::getUserData()->getUserId());
+ $Data->addParam($token);
+ $Data->addParam($this->itemData->getAuthtokenKey());
+ $Data->addParam($this->itemData->getAuthtokenPass());
+ $Data->addParam(Hash::hashKey($this->itemData->getAuthtokenHash()));
+ $Data->setOnErrorMessage(__('Error interno', false));
+
+ DB::getQuery($Data);
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ * @throws SPException
+ */
+ public function checkDuplicatedOnAdd()
+ {
+ $query = /** @lang SQL */
+ 'SELECT authtoken_id FROM authTokens
+ WHERE authtoken_userId = ?
+ AND authtoken_actionId = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($this->itemData->getAuthtokenUserId());
+ $Data->addParam($this->itemData->getAuthtokenActionId());
+
+ DB::getResults($Data);
+
+ return $Data->getQueryNumRows() === 1;
+ }
+
+ /**
+ * Obtener el token de la API de un usuario
+ *
+ * @param $id
+ * @return bool
+ */
+ private function getTokenByUserId($id)
+ {
+ $query = /** @lang SQL */
+ 'SELECT authtoken_token FROM authTokens WHERE authtoken_userId = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($id);
+
+ $queryRes = DB::getResults($Data);
+
+ return $Data->getQueryNumRows() === 1 ? $queryRes->authtoken_token : $this->generateToken();
+ }
+
+ /**
+ * Generar un token de acceso
+ *
+ * @return string
+ */
+ private function generateToken()
+ {
+ return Util::generateRandomBytes(32);
+ }
+
+ /**
+ * @param $id int
+ * @return $this
+ * @throws \SP\Core\Exceptions\SPException
+ */
+ public function delete($id)
+ {
+ $query = /** @lang SQL */
+ 'DELETE FROM authTokens WHERE authtoken_id = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($id);
+ $Data->setOnErrorMessage(__('Error interno', false));
+
+ DB::getQuery($Data);
+
+ if ($Data->getQueryNumRows() === 0) {
+ throw new SPException(SPException::SP_INFO, __('Token no encontrado', false));
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ * @throws \SP\Core\Exceptions\QueryException
+ * @throws \SP\Core\Exceptions\ConstraintException
+ * @throws \Defuse\Crypto\Exception\CryptoException
+ * @throws \SP\Core\Exceptions\SPException
+ */
+ public function update()
+ {
+ if ($this->checkDuplicatedOnUpdate()) {
+ throw new SPException(SPException::SP_WARNING, __('La autorización ya existe', false));
+ }
+
+ $token = $this->getTokenByUserId($this->itemData->getAuthtokenUserId());
+ $this->setSecureData($token);
+
+ $query = /** @lang SQL */
+ 'UPDATE authTokens
+ SET authtoken_userId = ?,
+ authtoken_actionId = ?,
+ authtoken_createdBy = ?,
+ authtoken_token = ?,
+ authtoken_key = ?,
+ authtoken_pass = ?,
+ authtoken_hash = ?,
+ authtoken_startDate = UNIX_TIMESTAMP()
+ WHERE authtoken_id = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($this->itemData->getAuthtokenUserId());
+ $Data->addParam($this->itemData->getAuthtokenActionId());
+ $Data->addParam(Session::getUserData()->getUserId());
+ $Data->addParam($token);
+ $Data->addParam($this->itemData->getAuthtokenKey());
+ $Data->addParam($this->itemData->getAuthtokenPass());
+ $Data->addParam(Hash::hashKey($this->itemData->getAuthtokenHash()));
+ $Data->addParam($this->itemData->getAuthtokenId());
+ $Data->setOnErrorMessage(__('Error interno', false));
+
+ DB::getQuery($Data);
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ * @throws \SP\Core\Exceptions\SPException
+ */
+ public function checkDuplicatedOnUpdate()
+ {
+ $query = /** @lang SQL */
+ 'SELECT authtoken_id FROM authTokens
+ WHERE authtoken_userId = ?
+ AND authtoken_actionId = ?
+ AND authtoken_id <> ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($this->itemData->getAuthtokenUserId());
+ $Data->addParam($this->itemData->getAuthtokenActionId());
+ $Data->addParam($this->itemData->getAuthtokenId());
+
+ DB::getResults($Data);
+
+ return $Data->getQueryNumRows() === 1;
+ }
+
+ /**
+ * Regenerar el hash de los tokens de un usuario
+ *
+ * @throws \SP\Core\Exceptions\ConstraintException
+ * @throws \SP\Core\Exceptions\QueryException
+ * @throws \Defuse\Crypto\Exception\CryptoException
+ */
+ public function refreshToken()
+ {
+ $token = $this->generateToken();
+ $this->setSecureData($token);
+
+ $query = /** @lang SQL */
+ 'UPDATE authTokens
+ SET authtoken_token = ?,
+ authtoken_hash = ?,
+ authtoken_key = ?,
+ authtoken_pass = ?,
+ authtoken_startDate = UNIX_TIMESTAMP()
+ WHERE authtoken_userId = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($this->generateToken());
+ $Data->addParam(Hash::hashKey($this->itemData->getAuthtokenHash()));
+ $Data->addParam($this->itemData->getAuthtokenKey());
+ $Data->addParam($this->itemData->getAuthtokenPass());
+ $Data->addParam($this->itemData->getAuthtokenUserId());
+ $Data->setOnErrorMessage(__('Error interno', false));
+
+ DB::getQuery($Data);
+
+ return $this;
+ }
+
+ /**
+ * Generar la llave segura del token
+ *
+ * @param $token
+ * @throws \Defuse\Crypto\Exception\CryptoException
+ */
+ private function setSecureData($token)
+ {
+ $key = $this->itemData->getAuthtokenHash() . $token;
+ $securedKey = Crypt::makeSecuredKey($key);
+
+ $this->itemData->setAuthtokenKey($securedKey);
+ $this->itemData->setAuthtokenPass(Crypt::encrypt(CryptSession::getSessionKey(), $securedKey, $key));
+ }
+
+ /**
+ * @param $id int
+ * @return ApiTokenData
+ */
+ public function getById($id)
+ {
+ $query = /** @lang SQL */
+ 'SELECT authtoken_id,
+ authtoken_userId,
+ authtoken_actionId,
+ authtoken_createdBy,
+ authtoken_startDate,
+ authtoken_token
+ FROM authTokens
+ WHERE authtoken_id = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setMapClassName($this->getDataModel());
+ $Data->setQuery($query);
+ $Data->addParam($id);
+
+ return DB::getResults($Data);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getAll()
+ {
+ // TODO: Implement getAll() method.
+ }
+
+ /**
+ * @param $id int
+ * @return mixed
+ */
+ public function checkInUse($id)
+ {
+ // TODO: Implement checkInUse() method.
+ }
+
+ /**
+ * Eliminar elementos en lote
+ *
+ * @param array $ids
+ * @return $this
+ * @throws \SP\Core\Exceptions\QueryException
+ * @throws \SP\Core\Exceptions\ConstraintException
+ */
+ public function deleteBatch(array $ids)
+ {
+ $query = /** @lang SQL */
+ 'DELETE FROM authTokens WHERE authtoken_id IN (' . $this->getParamsFromArray($ids) . ')';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->setParams($ids);
+ $Data->setOnErrorMessage(__('Error interno', false));
+
+ DB::getQuery($Data);
+
+ return $this;
+ }
+
+ /**
+ * Devolver los elementos con los ids especificados
+ *
+ * @param array $ids
+ * @return mixed
+ */
+ public function getByIdBatch(array $ids)
+ {
+ // TODO: Implement getByIdBatch() method.
+ }
+
+ /**
+ * Obtener el usuario a partir del token
+ *
+ * @param $token string El token de autorización
+ * @return bool|mixed
+ * @throws \SP\Core\Exceptions\SPException
+ */
+ public function getUserIdForToken($token)
+ {
+ $query = /** @lang SQL */
+ 'SELECT authtoken_userId FROM authTokens WHERE authtoken_token = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setQuery($query);
+ $Data->addParam($token);
+
+ $queryRes = DB::getResults($Data);
+
+ return $Data->getQueryNumRows() === 1 ? $queryRes->authtoken_userId : false;
+ }
+
+ /**
+ * Devolver los datos de un token
+ *
+ * @param $actionId int El id de la accion
+ * @param $token string El token de seguridad
+ * @return false|ApiTokenData
+ * @throws \SP\Core\Exceptions\SPException
+ */
+ public function getTokenByToken($actionId, $token)
+ {
+ $query = /** @lang SQL */
+ 'SELECT authtoken_userId,
+ authtoken_key,
+ authtoken_pass,
+ authtoken_hash
+ FROM authTokens
+ WHERE authtoken_actionId = ?
+ AND authtoken_token = ? LIMIT 1';
+
+ $Data = new QueryData();
+ $Data->setMapClassName($this->getDataModel());
+ $Data->setQuery($query);
+ $Data->addParam($actionId);
+ $Data->addParam($token);
+
+ $queryRes = DB::getResults($Data);
+
+ return $Data->getQueryNumRows() === 1 ? $queryRes : false;
+ }
+}
\ No newline at end of file
diff --git a/inc/SP/Mgmt/ApiTokens/ApiTokenBase.class.php b/inc/SP/Mgmt/ApiTokens/ApiTokenBase.class.php
new file mode 100644
index 00000000..dcfa6ff3
--- /dev/null
+++ b/inc/SP/Mgmt/ApiTokens/ApiTokenBase.class.php
@@ -0,0 +1,66 @@
+.
+ */
+
+namespace SP\Mgmt\ApiTokens;
+
+defined('APP_ROOT') || die();
+
+use SP\DataModel\ApiTokenData;
+use SP\Mgmt\ItemBase;
+
+/**
+ * Class ApiTokensBase
+ *
+ * @package SP\Mgmt\ApiTokens
+ */
+abstract class ApiTokenBase extends ItemBase
+{
+ /** @var ApiTokenData */
+ protected $itemData;
+
+ /**
+ * ApiTokensBase constructor.
+ *
+ * @param $itemData
+ * @throws \SP\Core\Exceptions\InvalidClassException
+ */
+ public function __construct($itemData = null)
+ {
+ if (!$this->dataModel) {
+ $this->setDataModel(ApiTokenData::class);
+ }
+
+ parent::__construct($itemData);
+ }
+
+ /**
+ * Devolver los datos del elemento
+ *
+ * @return ApiTokenData
+ */
+ public function getItemData()
+ {
+ return parent::getItemData();
+ }
+}
\ No newline at end of file
diff --git a/inc/SP/Mgmt/ApiTokens/ApiTokenSearch.class.php b/inc/SP/Mgmt/ApiTokens/ApiTokenSearch.class.php
new file mode 100644
index 00000000..60859649
--- /dev/null
+++ b/inc/SP/Mgmt/ApiTokens/ApiTokenSearch.class.php
@@ -0,0 +1,83 @@
+.
+ */
+
+namespace SP\Mgmt\ApiTokens;
+
+use SP\Core\Acl;
+use SP\DataModel\ItemSearchData;
+use SP\Mgmt\ItemSearchInterface;
+use SP\Storage\DB;
+use SP\Storage\QueryData;
+
+/**
+ * Class ApiTokenSearch
+ *
+ * @package SP\Mgmt\ApiTokens
+ */
+class ApiTokenSearch extends ApiTokenBase implements ItemSearchInterface
+{
+ /**
+ * @param ItemSearchData $SearchData
+ * @return mixed
+ */
+ public function getMgmtSearch(ItemSearchData $SearchData)
+ {
+ $query = 'SELECT authtoken_id,' .
+ 'authtoken_userId,' .
+ 'authtoken_actionId, ' .
+ 'authtoken_token, ' .
+ 'user_login ' .
+ 'FROM authTokens ' .
+ 'LEFT JOIN usrData ON user_id = authtoken_userId ';
+
+ $Data = new QueryData();
+
+ if ($SearchData->getSeachString() !== '') {
+ $search = '%' . $SearchData->getSeachString() . '%';
+ $query .= ' WHERE user_login LIKE ?';
+
+ $Data->addParam($search);
+ }
+
+ $query .= ' ORDER BY user_login';
+ $query .= ' LIMIT ?, ?';
+
+ $Data->addParam($SearchData->getLimitStart());
+ $Data->addParam($SearchData->getLimitCount());
+
+ $Data->setQuery($query);
+
+ DB::setFullRowCount();
+
+ $queryRes = DB::getResultsArray($Data);
+
+ foreach ($queryRes as $token) {
+ $token->authtoken_actionId = Acl::getActionName($token->authtoken_actionId);
+ }
+
+ $queryRes['count'] = $Data->getQueryNumRows();
+
+ return $queryRes;
+ }
+}
\ No newline at end of file
diff --git a/inc/SP/Storage/DB.class.php b/inc/SP/Storage/DB.class.php
index cc15ee4d..d0573649 100644
--- a/inc/SP/Storage/DB.class.php
+++ b/inc/SP/Storage/DB.class.php
@@ -272,19 +272,18 @@ class DB
/**
* Método para registar los eventos de BD en el log
*
- * @param $query string La consulta que genera el error
- * @param $errorMsg string El mensaje de error
- * @param $errorCode int El código de error
- * @param $queryFunction
+ * @param string $query La consulta que genera el error
+ * @param \Exception $e
+ * @param string $queryFunction
*/
- private static function logDBException($query, $errorMsg, $errorCode, $queryFunction)
+ private static function logDBException($query, \Exception $e, $queryFunction)
{
$caller = Util::traceLastCall($queryFunction);
$LogMessage = new LogMessage();
$LogMessage->setAction($caller);
$LogMessage->addDescription(__('Error en la consulta', false));
- $LogMessage->addDescription(sprintf('%s (%s)', $errorMsg, $errorCode));
+ $LogMessage->addDescription(sprintf('%s (%s)', $e->getMessage(), $e->getCode()));
$LogMessage->addDetails('SQL', DBUtil::escape($query));
debugLog($LogMessage->getDescription(), true);
@@ -345,7 +344,7 @@ class DB
} catch (SPException $e) {
$queryData->setQueryStatus($e->getCode());
- self::logDBException($queryData->getQuery(), $e->getMessage(), $e->getCode(), __FUNCTION__);
+ self::logDBException($queryData->getQuery(), $e, __FUNCTION__);
if ($e->getCode() === 23000) {
throw new ConstraintException(SPException::SP_ERROR, __('Restricción de integridad', false), $e->getMessage(), $e->getCode());
diff --git a/inc/sql/20117022101.sql b/inc/sql/20117022101.sql
index 5986b296..d14a12ca 100644
--- a/inc/sql/20117022101.sql
+++ b/inc/sql/20117022101.sql
@@ -7,4 +7,8 @@ ALTER TABLE `customFieldsData`
ALTER TABLE `usrData`
CHANGE COLUMN `user_mPass` `user_mPass` VARBINARY(1000) NULL DEFAULT NULL,
CHANGE COLUMN `user_mIV` `user_mKey` VARBINARY(1000) NULL DEFAULT NULL;
+ALTER TABLE `authTokens`
+ ADD COLUMN `authtoken_pass` VARBINARY(1000) NULL,
+ ADD COLUMN `authtoken_key` VARBINARY(1000) NULL,
+ ADD COLUMN `authtoken_hash` VARBINARY(100) NULL;
diff --git a/inc/themes/material-blue/css/styles.css b/inc/themes/material-blue/css/styles.css
index a9c7ef83..74d1c980 100644
--- a/inc/themes/material-blue/css/styles.css
+++ b/inc/themes/material-blue/css/styles.css
@@ -530,8 +530,8 @@ pre, code, samp, kbd {
padding: .3em 0; }
#box-popup {
- min-width: 25em;
- max-width: 50em;
+ min-width: 30em;
+ max-width: 60em;
margin: 5em auto;
padding: 0;
background-color: #fff; }
diff --git a/inc/themes/material-blue/css/styles.min.css b/inc/themes/material-blue/css/styles.min.css
index 70a1794a..3b9aca69 100644
--- a/inc/themes/material-blue/css/styles.min.css
+++ b/inc/themes/material-blue/css/styles.min.css
@@ -1 +1 @@
-html,body{margin:0;padding:0;text-align:left;background-color:#f5f5f5;color:#555;font-size:12px;font-weight:normal;box-sizing:border-box}*{font-family:"Roboto Regular",Verdana,Tahoma,sans-serif;box-sizing:inherit}*:before,*:after{box-sizing:inherit}table{font-size:11px;border-spacing:0}table th{border-bottom:2px solid transparent;vertical-align:middle}table th .icon{width:24px;height:24px}table tr{height:20px}table tr.odd{background-color:#f9f9f9}table tr.even>td,table tr.odd>td{border-bottom:1px solid #d9d9d9!important}table tr.even:hover,table tr.odd:hover{background-color:#e8ff99}table td{padding:3px}table td.txtCliente{font-weight:bold;text-align:center}form{font-size:11px;margin:0}input.inputImg,img.inputImg{background-color:transparent!important;width:24px!important;height:24px!important;border:0;vertical-align:middle;margin:0 .5em}input.txtFile{width:200px}input.txtLong{width:300px}textarea{width:350px;resize:none}select.files{width:250px}input.spinner{width:5em}img{margin:0;padding:0;border:0;cursor:pointer}img.inputImgMini{background-color:transparent!important;width:16px!important;height:16px!important;margin:0 5px 0 5px;border:0;vertical-align:middle}i{cursor:pointer}form .form-field{display:flex;justify-content:space-between}form .form-field>label{min-width:12em;padding:.5em 0;font-size:16px;align-self:center}form .form-field>div{width:100%;align-self:center}a{text-decoration:none;color:#536dfe}a:visited{text-decoration:none;color:#536dfe}a:hover,a:active,a:focus{text-decoration:none;cursor:pointer}pre,code,samp,kbd{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;font-size:1em;direction:ltr;text-align:left;background-color:#fbfaf9;color:#333;box-shadow:inset 0 0 .3em #ccc;border-radius:2px}#nojs{width:80%;text-align:center;vertical-align:middle;margin:10px auto;padding:3px;background-color:#ef5350;color:white;font-weight:bold;font-size:14px}#wrap{height:auto!important;min-height:100%;width:100%;background-color:#f5f5f5}#wrap-loading{position:fixed;z-index:9999;top:50%;left:50%;padding:1em;background-color:rgba(255,255,255,0.8);display:none;border-radius:5px!important;-moz-border-radius:5px!important;-webkit-border-radius:5px!important}#wrap-loading.overlay-full{top:0;left:0;width:100%;height:100%;background-color:rgba(255,255,255,0.5)}#wrap-loading.overlay-full #loading{position:absolute;top:50%;left:50%}#container{margin:auto;width:100%}#container.login{padding-top:5%}#container.error,#container.install,#container.passreset{width:100%}#container .logo{height:64px}#container #actions-bar{z-index:100;display:flex;justify-content:space-between;position:fixed;border:0 none;top:0;left:0;width:100%;padding:1em 0;background-color:transparent}#container #actions-bar-icons{flex-grow:1;text-align:center}#container #actions-bar-logo{display:none;padding:0 .5em}#container #actions-bar-logo img{display:inline-block;width:50px;opacity:.75}#container #content{width:95%;margin:2em auto 8em auto}#container #content.public-link{width:70%;min-height:0;margin:5em auto}#content td.descField,#box-popup td.descField{text-align:right;padding-right:20px;width:25%;border-right:1px solid #d9d9d9;color:#999;font-size:12px;font-weight:bold}#content td.valField,#box-popup td.valField{padding-left:1em;width:100%}#content td.valField .lowres-title,#box-popup td.valField .lowres-title{display:none;width:100%;color:#607d8b;font-size:12px}#content .pager{width:100%;margin-top:15px;padding:.5em;vertical-align:middle;font-size:11px;color:#999;background-color:#fcfcfc}#content .pager img{margin-left:5px;vertical-align:middle}#content .pager a{margin-left:5px;font-size:12px;color:#999}#content .pager>div{display:inline-block;width:49%}#content .pager .pager-left{text-align:left}#content .pager .pager-right{text-align:right}#content #title{width:50%;padding:7px;margin:auto;background-color:#d9d9d9;color:#fff;font-size:17px;letter-spacing:.3em;text-align:center}#content #title.titleNormal{background-color:#607d8b;color:#fff}#content .data-container{width:75%;margin:0 auto}#content fieldset.data{margin:2em auto}#content fieldset.data>legend{color:#607d8b;padding:0 .5em;font-size:1.5em}#content fieldset.data>div{display:none}#content fieldset.data>div table{width:100%}#content .data{width:100%;padding:10px;border:1px solid #c9c9c9;margin:0 auto;background-color:#f9f9f9}#content .data #history-icon{position:relative;top:5em;right:2em}#content .data td{text-align:left}#content .data td.descField{text-align:right}#content .data select{min-width:210px}#content .data .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#content .data .dropzone{width:30em;padding:1em;border:2px dashed #26a69a;text-align:center}#content .data .dropzone img{vertical-align:middle}#content .data .file-upload{display:none}#content .data .account-permissions{width:100%}#content .data .account-permissions fieldset{border:1px solid #c9c9c9;padding:1em}#content .data .account-permissions legend{font-weight:bold;color:#999;padding:.2em 0}#content .data .account-permissions fieldset>span{font-weight:bold;color:#999;padding:.2em 0;display:inline-block;width:100px;text-align:right}#content span.tag{margin:0 3px 3px 0;padding:.2em;background:#5c6bc0;color:#fff;border:0 solid transparent;border-radius:3px!important;-moz-border-radius:3px!important;-webkit-border-radius:3px!important}#content .extra-info{margin-top:20px}#content #tabs fieldset{border:1px solid #c9c9c9}#content #tabs #frmConfig label{float:left}#content .tblConfig{margin-bottom:2em}#content .tblConfig td.descField{width:35%;font-size:11px;font-weight:bold}#content .tblConfig td.rowHeader{padding:5px 0 5px 0;background-color:#f5f5f5;text-align:center;font-weight:bold;border-top:15px solid #f9f9f9;border-bottom:3px solid #a9c1d7;letter-spacing:.5em;color:#696969}#content .tblConfig input.checkbox{width:15px;text-align:left;padding:0}#content .tblConfig .option-disabled{text-align:center;background-color:#fff8e1;color:#ffca28;font-weight:bold}#content h2{width:100%;height:1.5em;font-size:18px;color:white;background-color:#a9c1d7;margin:0;padding-top:.1em}#content .section{margin-top:2.5em;border-bottom:1px solid #d9d9d9;text-align:left;font-size:14px;font-weight:bold;color:#5c6bc0}#content .row_even>td{background-color:#f5f5f5}#content .row_odd>td{background-color:white}#content .data-header ul{list-style:none;width:100%;margin:0 0 10px 0;padding:0}#content .data-header li{display:inline-block;padding:.2em .5em;font-weight:bold;letter-spacing:.2em;color:#fff;text-align:center}#content .data-header li a{color:#777}#content .data-header li img{float:right;width:24px;height:24px;vertical-align:middle}#content .data-header-minimal{border-bottom:1px solid #dfdfdf}#content .data-header-minimal ul{display:flex;flex-wrap:wrap;justify-content:flex-start;margin:0}#content .data-header-minimal li{display:inline-flex;min-width:10em;font-weight:normal;letter-spacing:normal}#content .data-header-minimal li a{color:#b9b9b9;padding:.3em .8em}#content .data-table{width:100%}#content .data-table td:first-of-type,#content .data-table th:first-of-type{width:5em}#content .data-table thead th{background-color:#607d8b;color:#fff}#content .data-table tbody td.cell-data{text-align:left}#content .data-table tbody td.cell-nodata{padding:0 .5em;text-align:left}#content .data-table tbody td.cell-actions{text-align:right}#content .data-table tbody td.cell-actions i{opacity:.5}#content .data-table tbody td.cell-actions i:hover{opacity:1}#content .data-rows ul{display:table;list-style:none;width:100%;margin:0 0 10px 0;padding:0;background-color:#fcfcfc}#content .data-rows li{float:left;display:block;padding:1em;color:#696969;text-align:center;min-height:2em}#content .data-rows li.cell-nodata{padding:1em 0;min-height:2em;text-align:left}#content .data-rows li.cell-actions{float:right;min-height:2em;padding:1em 0;text-align:left;background-color:#fcfcfc;width:15em}#content .data-rows li.cell-nodata img,#content .data-rows li.cell-actions img{width:24px;height:24px;margin:0 .5em}#content #resEventLog .data{width:100%}#content #resEventLog thead{text-align:center}#content #resEventLog tbody{width:100%;height:500px;overflow:auto}#content #resEventLog td{border-bottom:1px solid #d9d9d9}#content #resEventLog .cell{text-align:center}#content #resEventLog .cell-description{width:60%}#content #searchbox{background-color:#fcfcfc;vertical-align:middle;position:relative;height:auto;padding:.5em 1em;margin-bottom:2em}#content #searchbox form{display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;text-align:left}#content #searchbox .search-filters>*{margin:0 1em}#content #searchbox .search-filters .filter-buttons{display:inline-block}#content #searchbox .search-filters .filter-slider{width:10em}#content #searchbox .search-filters-tags{display:none;flex-grow:2}#content .btn-clear{opacity:.35;filter:alpha(opacity=35)}#content .btn-clear:hover{opacity:1;filter:alpha(opacity=100)}#content .actions-optional{display:none}#content .error{width:350px;padding:15px;margin:0 auto;text-align:center;font-size:16px;line-height:1.5em;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}#box-popup .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#content .data .list-wrap ul,#box-popup .list-wrap ul{list-style-type:none;margin:0;padding:0}#content .data .list-wrap li,#box-popup .list-wrap li{display:flex;background:#f2f2f2;padding:.5em;font-size:1em;margin-bottom:.5em}#content .data .list-wrap li:hover,#box-popup .list-wrap li:hover{background:#e8eaf6;color:#000}#content .data .list-wrap div.files-item-info,#box-popup .list-wrap div.files-item-info{flex-grow:2}#content .data .list-wrap div.files-item-info img,#box-popup .list-wrap div.files-item-info img{margin:0 .5em}#content .data .list-wrap div.files-item-actions,#box-popup .list-wrap div.files-item-actions{padding:.3em 0}#box-popup{min-width:25em;max-width:50em;margin:5em auto;padding:0;background-color:#fff}#box-popup.box-password-view{min-width:30em;max-width:35em}#box-popup>h2{position:relative;width:100%;font-size:18px;color:#fff;background-color:#607d8b;margin:0;padding:.5em 0;line-height:1em}#box-popup>h2 .btn-popup-close{display:none;position:absolute;right:.5em;top:.2em}#box-popup>table{width:100%;padding-bottom:1em}#box-popup select{width:220px}#box-popup #resFancyAccion{display:none}#box-popup #resCheck{display:inline-block;width:80%;height:4em;padding:1em 0}#box-popup.image{background-color:transparent;max-width:100%;margin:0 auto;border-radius:0!important;-moz-border-radius:0!important;-webkit-border-radius:0!important}#box-popup.image img{width:auto;margin:0 auto}#box-popup.image>div.title{background-color:#607d8b;color:#fff;padding:.5em}#box-popup.help{min-height:100px;background-color:#f5f5f5}#box-popup.help p{font-size:14px;text-align:justify;line-height:2em}#box-complexity>div{text-align:justify;line-height:1.5em;margin-top:1em}#debug{float:left;text-align:left}#debuginfo{width:100%;min-height:10em;padding:1em;background-color:#fff8e1;text-align:left;line-height:1.5em}#debuginfo H3{text-align:center}.popup-data{width:100%;min-width:400px;border:0;text-align:left;margin:0;padding:1em .5em}.popup-data .descField{min-width:100px}footer{display:flex;justify-content:space-between;position:fixed;bottom:0;z-index:100;width:100%;padding:.5em 0;background-color:#f5f5f5;color:#b9b9b9;font-size:1em;-webkit-box-shadow:0 -3px 2px -2px rgba(0,0,0,0.14);-moz-box-shadow:0 -3px 2px -2px rgba(0,0,0,0.14);box-shadow:0 -3px 2px -2px rgba(0,0,0,0.14)}footer .footer-parts{display:flex;justify-content:space-between}footer #footer-left{width:50%;margin:0 1em}footer #footer-right{width:50%;margin:0 1em;justify-content:flex-end;text-align:right}footer #updates{min-width:10em;text-align:center;cursor:pointer}footer #status{margin:0 1em}footer #status>div{display:inline-block}footer #status .status-info{padding:.5em}footer #session{text-align:left;color:#999;font-size:.8em}footer a{color:#b9b9b9}footer a:visited{color:#b9b9b9}footer #project a:hover{color:#a9c1d7;border-bottom:1px solid #a9c1d7}footer #updates a:hover{color:#a9c1d7}footer img{border:0;width:16px;height:16px;vertical-align:middle}.round,.round5{border-radius:5px!important;-moz-border-radius:5px!important;-webkit-border-radius:5px!important}.midround{border-radius:0 0 10px 10px!important;-moz-border-radius:0 0 10px 10px!important;-webkit-border-radius:0 0 10px 10px!important}.midroundup{border-radius:10px 10px 0 0!important;-moz-border-radius:10px 10px 0 0!important;-webkit-border-radius:10px 10px 0 0!important}.fullround{border-radius:50%!important;-moz-border-radius:50%!important;-webkit-border-radius:50%!important}.iconMini{width:16px!important;height:16px!important;vertical-align:middle}.hide{display:none!important}.btn-checks{padding:5px;margin:.2em 0;width:30em;border-bottom:1px solid #c9c9c9}.shadow{-webkit-box-shadow:2px 2px 3px -3px rgba(0,0,0,0.14);-moz-box-shadow:2px 2px 3px -3px rgba(0,0,0,0.14);box-shadow:1px 1px 2px rgba(0,0,0,0.14)}.noRes{width:60%;padding:15px;background-color:#f9f9f9;color:#a9a9a9;border:#c9c9c9 1px solid;margin:20px auto;text-align:center;font-size:16px}.header-grey{background-color:#607d8b;color:#fff;min-height:2em}.no-background{background:none!important}.action-in-box{padding:1em;text-align:right}.action-in-box ul{list-style:none;margin:0;padding:0}.tab-data{margin:2em auto 0;width:75%}.item-actions{margin:1em auto}.tab-actions{margin:2em 0}.item-actions>ul,.tab-actions>ul{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;list-style:none;margin:0;padding:0}.item-actions>ul>li,.tab-actions>ul>li{width:auto;min-width:2em;margin-left:.5em}.item-actions>ul>li.datagrid-action-search,.tab-actions>ul>li.datagrid-action-search{min-width:5em}.item-actions>ul>li.datagrid-action-search form,.tab-actions>ul>li.datagrid-action-search form{width:100%}.fullWidth{max-width:100%!important}.filter-on{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;padding:.3em 1em}.global-on{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;padding:.3em 1em}.opacity50{filter:alpha(opacity=50);opacity:.5}.custom-combobox{position:relative;display:inline-block}.custom-combobox input{width:80%}.custom-combobox-toggle{position:absolute;top:0;bottom:0;margin-left:-1px;padding:0;*height:1.7em;*top:.1em}.custom-combobox-input{margin:0;padding:.3em}.passLevel{width:20px;height:20px;display:inline-block;position:relative;top:2px}.passLevel.strongest{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strongest:hover{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strong{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.strong:hover{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.good{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.good:hover{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.weak{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}.passLevel.weak:hover{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}#alert #alert-text{margin:15px auto;font-size:14px;font-weight:bold}#alert #alert-pass{width:50%;padding:10px;margin:15px auto;border:1px solid #c9c9c9;color:#555;font-weight:bold}.dialog-text,.dialog-user-text,.dialog-pass-text{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;padding:.5em;text-align:center;min-width:200px}.dialog-user-text{border-bottom:#d9d9d9 1px solid;color:#a9a9a9}.dialog-pass-text{border:transparent 1px solid;letter-spacing:.2em}.dialog-buttons{text-align:center;padding:.5em;border-top:1px solid #c9c9c9;line-height:2.5em}.dialog-clip-copy{color:#26a69a;background-color:#e0f2f1}.help-box{display:none;background-color:#fff!important;color:#607d8b}.help-box>*{font-weight:bold}.help-text{text-align:justify;line-height:1.5em;margin-top:1em}.tooltip{width:300px;max-width:300px;background-color:#777;color:#fff;z-index:101}.cursor-pointer{cursor:pointer}.password-actions{display:inline-block;width:12em}.password-actions>span,.password-actions i{margin-right:.6em}.custom-input-color{width:3em;height:1em;display:inline-block}.account-pass-image{height:32px;width:auto}.select-box{min-width:20em}fieldset.warning{padding:8px;border-radius:5px;color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}fieldset.warning legend{color:#ef5350!important}fieldset.warning a{color:#ef5350!important;font-weight:bold}#actions{width:100%;line-height:2em;margin-bottom:5em}#actions #logo{display:flex;width:100%;margin-bottom:30px;color:#607d8b;align-items:center;background:url("../imgs/logo_full_bg.png") left no-repeat;background-size:auto 150px;height:150px}#actions #page-title{width:100%;color:#607d8b;text-align:center}#actions #page-title h1{font-weight:bold;font-size:24px;letter-spacing:3px}#actions ul.errors{max-width:40vw;margin:0 auto;list-style:none;font-size:14px;text-align:left}#actions ul.errors>li{margin:1.5em auto;border-radius:3px;padding:1em .5em}#actions ul.errors>li.msg-critical{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}#actions ul.errors>li.msg-warning{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;color:#555}#actions ul.errors>li.msg-ok{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#actions ul.errors>li>p.hint{color:#555;font-size:12px}#actions ul.errors>li>p.hint i{margin-right:.5em}#actions form{width:450px;margin:0 auto;text-align:left}#actions form fieldset{margin-bottom:2em}#actions form fieldset legend{width:100%;color:#fff;font-size:14px;font-weight:bold;text-align:center;background-color:#607d8b;margin:1em 0;letter-spacing:.2em;padding:.2em 0}#actions div.buttons{margin-top:2em;text-align:center}.center{text-align:center!important}.right{text-align:right!important}.left{text-align:left!important}.opacity50{opacity:.5!important}#login-container{width:40em;margin:0 auto;background:transparent url("../imgs/logo_full_bg.png") no-repeat top left;background-size:auto 10em}#login-container #boxSpacer{height:11em;background-color:transparent}#login-container #boxLogin{position:relative;margin:0 auto;width:100%;min-height:14em;padding:1em;background-color:#fff}#login-container #boxLogin #boxData{height:100%;min-height:14em;text-align:left;background-color:transparent}#login-container #boxLogin #boxData i{margin-right:.5em;opacity:.5}#login-container #boxLogin #boxData .extra-hidden{display:none}#login-container #boxLogin #boxButton{position:absolute;top:2em;right:2em}#login-container #boxLogin #boxActions{width:100%;text-align:right}#login-container #boxLogin #boxActions a{color:#c9c9c9}#login-container #boxLogout{margin-top:4em;width:100%}#login-container #boxLogout>div{margin:0 auto;width:250px;font-size:14px;padding:.5em;text-align:center;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}#login-container #boxUpdated{width:350px;margin:3em auto;font-size:14px;text-align:center;padding:.5em;color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#login-container #demo-info{margin:3em auto;color:#c9c9c9;border-top:1px solid #d9d9d9;border-bottom:1px solid #d9d9d9;padding:.5em}#login-container #demo-info ul{display:flex;justify-content:space-around;list-style:none}#login-container #demo-info ul li span{margin:0 2em}@media screen and (max-width:1000px){#content #searchbox .search-filters>*{margin:.5em 1em .5em 0}#content .data-container #title,#content .tab-data #title{width:90%}footer{display:none;justify-content:space-between;flex-wrap:wrap}footer .footer-parts{justify-content:space-between;flex-wrap:wrap}footer #footer-left,footer #footer-right{width:100%}footer .footer-parts>div{width:100%;padding:.5em 0}}@media screen and (max-width:600px){#content input,#box-popup input{width:100%}#content .mdl-textfield,#box-popup .mdl-textfield{width:100%}#content td.descField,#box-popup td.descField{display:none}#content td.valField .lowres-title,#box-popup td.valField .lowres-title{display:block}#content #searchbox .mdl-textfield{width:90%}#content #searchbox .search-text{width:90%}#content #searchbox .search-filters .selectize-control{width:100%}#content .data-container,#content .tab-data{width:100%}#content .data-container #title,#content .tab-data #title{width:90%}#content .data-container .selectize-control,#content .tab-data .selectize-control{width:100%}#box-popup h2>.btn-popup-close{display:inline-block}.mdl-data-table{table-layout:fixed;width:100%}.table-responsive td,.table-responsive th{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis}.table-responsive .cell-actions i{display:block!important}}
\ No newline at end of file
+html,body{margin:0;padding:0;text-align:left;background-color:#f5f5f5;color:#555;font-size:12px;font-weight:normal;box-sizing:border-box}*{font-family:"Roboto Regular",Verdana,Tahoma,sans-serif;box-sizing:inherit}*:before,*:after{box-sizing:inherit}table{font-size:11px;border-spacing:0}table th{border-bottom:2px solid transparent;vertical-align:middle}table th .icon{width:24px;height:24px}table tr{height:20px}table tr.odd{background-color:#f9f9f9}table tr.even>td,table tr.odd>td{border-bottom:1px solid #d9d9d9 !important}table tr.even:hover,table tr.odd:hover{background-color:#e8ff99}table td{padding:3px}table td.txtCliente{font-weight:bold;text-align:center}form{font-size:11px;margin:0}input.inputImg,img.inputImg{background-color:transparent !important;width:24px !important;height:24px !important;border:0;vertical-align:middle;margin:0 .5em}input.txtFile{width:200px}input.txtLong{width:300px}textarea{width:350px;resize:none}select.files{width:250px}input.spinner{width:5em}img{margin:0;padding:0;border:0;cursor:pointer}img.inputImgMini{background-color:transparent !important;width:16px !important;height:16px !important;margin:0 5px 0 5px;border:0;vertical-align:middle}i{cursor:pointer}form .form-field{display:flex;justify-content:space-between}form .form-field>label{min-width:12em;padding:.5em 0;font-size:16px;align-self:center}form .form-field>div{width:100%;align-self:center}a{text-decoration:none;color:#536dfe}a:visited{text-decoration:none;color:#536dfe}a:hover,a:active,a:focus{text-decoration:none;cursor:pointer}pre,code,samp,kbd{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;font-size:1em;direction:ltr;text-align:left;background-color:#fbfaf9;color:#333;box-shadow:inset 0 0 .3em #ccc;border-radius:2px}#nojs{width:80%;text-align:center;vertical-align:middle;margin:10px auto;padding:3px;background-color:#ef5350;color:white;font-weight:bold;font-size:14px}#wrap{height:auto !important;min-height:100%;width:100%;background-color:#f5f5f5}#wrap-loading{position:fixed;z-index:9999;top:50%;left:50%;padding:1em;background-color:rgba(255,255,255,0.8);display:none;border-radius:5px !important;-moz-border-radius:5px !important;-webkit-border-radius:5px !important}#wrap-loading.overlay-full{top:0;left:0;width:100%;height:100%;background-color:rgba(255,255,255,0.5)}#wrap-loading.overlay-full #loading{position:absolute;top:50%;left:50%}#container{margin:auto;width:100%}#container.login{padding-top:5%}#container.error,#container.install,#container.passreset{width:100%}#container .logo{height:64px}#container #actions-bar{z-index:100;display:flex;justify-content:space-between;position:fixed;border:0 none;top:0;left:0;width:100%;padding:1em 0;background-color:transparent}#container #actions-bar-icons{flex-grow:1;text-align:center}#container #actions-bar-logo{display:none;padding:0 .5em}#container #actions-bar-logo img{display:inline-block;width:50px;opacity:.75}#container #content{width:95%;margin:2em auto 8em auto}#container #content.public-link{width:70%;min-height:0;margin:5em auto}#content td.descField,#box-popup td.descField{text-align:right;padding-right:20px;width:25%;border-right:1px solid #d9d9d9;color:#999;font-size:12px;font-weight:bold}#content td.valField,#box-popup td.valField{padding-left:1em;width:100%}#content td.valField .lowres-title,#box-popup td.valField .lowres-title{display:none;width:100%;color:#607d8b;font-size:12px}#content .pager{width:100%;margin-top:15px;padding:.5em;vertical-align:middle;font-size:11px;color:#999;background-color:#fcfcfc}#content .pager img{margin-left:5px;vertical-align:middle}#content .pager a{margin-left:5px;font-size:12px;color:#999}#content .pager>div{display:inline-block;width:49%}#content .pager .pager-left{text-align:left}#content .pager .pager-right{text-align:right}#content #title{width:50%;padding:7px;margin:auto;background-color:#d9d9d9;color:#fff;font-size:17px;letter-spacing:.3em;text-align:center}#content #title.titleNormal{background-color:#607d8b;color:#fff}#content .data-container{width:75%;margin:0 auto}#content fieldset.data{margin:2em auto}#content fieldset.data>legend{color:#607d8b;padding:0 .5em;font-size:1.5em}#content fieldset.data>div{display:none}#content fieldset.data>div table{width:100%}#content .data{width:100%;padding:10px;border:1px solid #c9c9c9;margin:0 auto;background-color:#f9f9f9}#content .data #history-icon{position:relative;top:5em;right:2em}#content .data td{text-align:left}#content .data td.descField{text-align:right}#content .data select{min-width:210px}#content .data .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#content .data .dropzone{width:30em;padding:1em;border:2px dashed #26a69a;text-align:center}#content .data .dropzone img{vertical-align:middle}#content .data .file-upload{display:none}#content .data .account-permissions{width:100%}#content .data .account-permissions fieldset{border:1px solid #c9c9c9;padding:1em}#content .data .account-permissions legend{font-weight:bold;color:#999;padding:.2em 0}#content .data .account-permissions fieldset>span{font-weight:bold;color:#999;padding:.2em 0;display:inline-block;width:100px;text-align:right}#content span.tag{margin:0 3px 3px 0;padding:.2em;background:#5c6bc0;color:#fff;border:0 solid transparent;border-radius:3px !important;-moz-border-radius:3px !important;-webkit-border-radius:3px !important}#content .extra-info{margin-top:20px}#content #tabs fieldset{border:1px solid #c9c9c9}#content #tabs #frmConfig label{float:left}#content .tblConfig{margin-bottom:2em}#content .tblConfig td.descField{width:35%;font-size:11px;font-weight:bold}#content .tblConfig td.rowHeader{padding:5px 0 5px 0;background-color:#f5f5f5;text-align:center;font-weight:bold;border-top:15px solid #f9f9f9;border-bottom:3px solid #a9c1d7;letter-spacing:.5em;color:#696969}#content .tblConfig input.checkbox{width:15px;text-align:left;padding:0}#content .tblConfig .option-disabled{text-align:center;background-color:#fff8e1;color:#ffca28;font-weight:bold}#content h2{width:100%;height:1.5em;font-size:18px;color:white;background-color:#a9c1d7;margin:0;padding-top:.1em}#content .section{margin-top:2.5em;border-bottom:1px solid #d9d9d9;text-align:left;font-size:14px;font-weight:bold;color:#5c6bc0}#content .row_even>td{background-color:#f5f5f5}#content .row_odd>td{background-color:white}#content .data-header ul{list-style:none;width:100%;margin:0 0 10px 0;padding:0}#content .data-header li{display:inline-block;padding:.2em .5em;font-weight:bold;letter-spacing:.2em;color:#fff;text-align:center}#content .data-header li a{color:#777}#content .data-header li img{float:right;width:24px;height:24px;vertical-align:middle}#content .data-header-minimal{border-bottom:1px solid #dfdfdf}#content .data-header-minimal ul{display:flex;flex-wrap:wrap;justify-content:flex-start;margin:0}#content .data-header-minimal li{display:inline-flex;min-width:10em;font-weight:normal;letter-spacing:normal}#content .data-header-minimal li a{color:#b9b9b9;padding:.3em .8em}#content .data-table{width:100%}#content .data-table td:first-of-type,#content .data-table th:first-of-type{width:5em}#content .data-table thead th{background-color:#607d8b;color:#fff}#content .data-table tbody td.cell-data{text-align:left}#content .data-table tbody td.cell-nodata{padding:0 .5em;text-align:left}#content .data-table tbody td.cell-actions{text-align:right}#content .data-table tbody td.cell-actions i{opacity:.5}#content .data-table tbody td.cell-actions i:hover{opacity:1}#content .data-rows ul{display:table;list-style:none;width:100%;margin:0 0 10px 0;padding:0;background-color:#fcfcfc}#content .data-rows li{float:left;display:block;padding:1em;color:#696969;text-align:center;min-height:2em}#content .data-rows li.cell-nodata{padding:1em 0;min-height:2em;text-align:left}#content .data-rows li.cell-actions{float:right;min-height:2em;padding:1em 0;text-align:left;background-color:#fcfcfc;width:15em}#content .data-rows li.cell-nodata img,#content .data-rows li.cell-actions img{width:24px;height:24px;margin:0 .5em}#content #resEventLog .data{width:100%}#content #resEventLog thead{text-align:center}#content #resEventLog tbody{width:100%;height:500px;overflow:auto}#content #resEventLog td{border-bottom:1px solid #d9d9d9}#content #resEventLog .cell{text-align:center}#content #resEventLog .cell-description{width:60%}#content #searchbox{background-color:#fcfcfc;vertical-align:middle;position:relative;height:auto;padding:.5em 1em;margin-bottom:2em}#content #searchbox form{display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;text-align:left}#content #searchbox .search-filters>*{margin:0 1em}#content #searchbox .search-filters .filter-buttons{display:inline-block}#content #searchbox .search-filters .filter-slider{width:10em}#content #searchbox .search-filters-tags{display:none;flex-grow:2}#content .btn-clear{opacity:.35;filter:alpha(opacity=35)}#content .btn-clear:hover{opacity:1;filter:alpha(opacity=100)}#content .actions-optional{display:none}#content .error{width:350px;padding:15px;margin:0 auto;text-align:center;font-size:16px;line-height:1.5em;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}#box-popup .list-wrap{max-height:10em;overflow:auto;padding:.5em;margin:1em 0}#content .data .list-wrap ul,#box-popup .list-wrap ul{list-style-type:none;margin:0;padding:0}#content .data .list-wrap li,#box-popup .list-wrap li{display:flex;background:#f2f2f2;padding:.5em;font-size:1em;margin-bottom:.5em}#content .data .list-wrap li:hover,#box-popup .list-wrap li:hover{background:#e8eaf6;color:#000}#content .data .list-wrap div.files-item-info,#box-popup .list-wrap div.files-item-info{flex-grow:2}#content .data .list-wrap div.files-item-info img,#box-popup .list-wrap div.files-item-info img{margin:0 .5em}#content .data .list-wrap div.files-item-actions,#box-popup .list-wrap div.files-item-actions{padding:.3em 0}#box-popup{min-width:30em;max-width:60em;margin:5em auto;padding:0;background-color:#fff}#box-popup.box-password-view{min-width:30em;max-width:35em}#box-popup>h2{position:relative;width:100%;font-size:18px;color:#fff;background-color:#607d8b;margin:0;padding:.5em 0;line-height:1em}#box-popup>h2 .btn-popup-close{display:none;position:absolute;right:.5em;top:.2em}#box-popup>table{width:100%;padding-bottom:1em}#box-popup select{width:220px}#box-popup #resFancyAccion{display:none}#box-popup #resCheck{display:inline-block;width:80%;height:4em;padding:1em 0}#box-popup.image{background-color:transparent;max-width:100%;margin:0 auto;border-radius:0 !important;-moz-border-radius:0 !important;-webkit-border-radius:0 !important}#box-popup.image img{width:auto;margin:0 auto}#box-popup.image>div.title{background-color:#607d8b;color:#fff;padding:.5em}#box-popup.help{min-height:100px;background-color:#f5f5f5}#box-popup.help p{font-size:14px;text-align:justify;line-height:2em}#box-complexity>div{text-align:justify;line-height:1.5em;margin-top:1em}#debug{float:left;text-align:left}#debuginfo{width:100%;min-height:10em;padding:1em;background-color:#fff8e1;text-align:left;line-height:1.5em}#debuginfo H3{text-align:center}.popup-data{width:100%;min-width:400px;border:0;text-align:left;margin:0;padding:1em .5em}.popup-data .descField{min-width:100px}footer{display:flex;justify-content:space-between;position:fixed;bottom:0;z-index:100;width:100%;padding:.5em 0;background-color:#f5f5f5;color:#b9b9b9;font-size:1em;-webkit-box-shadow:0 -3px 2px -2px rgba(0,0,0,0.14);-moz-box-shadow:0 -3px 2px -2px rgba(0,0,0,0.14);box-shadow:0 -3px 2px -2px rgba(0,0,0,0.14)}footer .footer-parts{display:flex;justify-content:space-between}footer #footer-left{width:50%;margin:0 1em}footer #footer-right{width:50%;margin:0 1em;justify-content:flex-end;text-align:right}footer #updates{min-width:10em;text-align:center;cursor:pointer}footer #status{margin:0 1em}footer #status>div{display:inline-block}footer #status .status-info{padding:.5em}footer #session{text-align:left;color:#999;font-size:.8em}footer a{color:#b9b9b9}footer a:visited{color:#b9b9b9}footer #project a:hover{color:#a9c1d7;border-bottom:1px solid #a9c1d7}footer #updates a:hover{color:#a9c1d7}footer img{border:0;width:16px;height:16px;vertical-align:middle}.round,.round5{border-radius:5px !important;-moz-border-radius:5px !important;-webkit-border-radius:5px !important}.midround{border-radius:0 0 10px 10px !important;-moz-border-radius:0 0 10px 10px !important;-webkit-border-radius:0 0 10px 10px !important}.midroundup{border-radius:10px 10px 0 0 !important;-moz-border-radius:10px 10px 0 0 !important;-webkit-border-radius:10px 10px 0 0 !important}.fullround{border-radius:50% !important;-moz-border-radius:50% !important;-webkit-border-radius:50% !important}.iconMini{width:16px !important;height:16px !important;vertical-align:middle}.hide{display:none !important}.btn-checks{padding:5px;margin:.2em 0;width:30em;border-bottom:1px solid #c9c9c9}.shadow{-webkit-box-shadow:2px 2px 3px -3px rgba(0,0,0,0.14);-moz-box-shadow:2px 2px 3px -3px rgba(0,0,0,0.14);box-shadow:1px 1px 2px rgba(0,0,0,0.14)}.noRes{width:60%;padding:15px;background-color:#f9f9f9;color:#a9a9a9;border:#c9c9c9 1px solid;margin:20px auto;text-align:center;font-size:16px}.header-grey{background-color:#607d8b;color:#fff;min-height:2em}.no-background{background:none !important}.action-in-box{padding:1em;text-align:right}.action-in-box ul{list-style:none;margin:0;padding:0}.tab-data{margin:2em auto 0;width:75%}.item-actions{margin:1em auto}.tab-actions{margin:2em 0}.item-actions>ul,.tab-actions>ul{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;list-style:none;margin:0;padding:0}.item-actions>ul>li,.tab-actions>ul>li{width:auto;min-width:2em;margin-left:.5em}.item-actions>ul>li.datagrid-action-search,.tab-actions>ul>li.datagrid-action-search{min-width:5em}.item-actions>ul>li.datagrid-action-search form,.tab-actions>ul>li.datagrid-action-search form{width:100%}.fullWidth{max-width:100% !important}.filter-on{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;padding:.3em 1em}.global-on{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;padding:.3em 1em}.opacity50{filter:alpha(opacity=50);opacity:.5}.custom-combobox{position:relative;display:inline-block}.custom-combobox input{width:80%}.custom-combobox-toggle{position:absolute;top:0;bottom:0;margin-left:-1px;padding:0;*height:1.7em;*top:.1em}.custom-combobox-input{margin:0;padding:.3em}.passLevel{width:20px;height:20px;display:inline-block;position:relative;top:2px}.passLevel.strongest{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strongest:hover{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a;font-weight:bold}.passLevel.strong{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.strong:hover{color:#2196f3;background-color:#e3f2fd;border:1px solid #2196f3;font-weight:bold}.passLevel.good{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.good:hover{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;font-weight:bold}.passLevel.weak{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}.passLevel.weak:hover{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350;font-weight:bold}#alert #alert-text{margin:15px auto;font-size:14px;font-weight:bold}#alert #alert-pass{width:50%;padding:10px;margin:15px auto;border:1px solid #c9c9c9;color:#555;font-weight:bold}.dialog-text,.dialog-user-text,.dialog-pass-text{font-family:Consolas,"Andale Mono WT","Andale Mono","Bitstream Vera Sans Mono","Nimbus Mono L",Monaco,"Courier New",monospace;padding:.5em;text-align:center;min-width:200px}.dialog-user-text{border-bottom:#d9d9d9 1px solid;color:#a9a9a9}.dialog-pass-text{border:transparent 1px solid;letter-spacing:.2em}.dialog-buttons{text-align:center;padding:.5em;border-top:1px solid #c9c9c9;line-height:2.5em}.dialog-clip-copy{color:#26a69a;background-color:#e0f2f1}.help-box{display:none;background-color:#fff !important;color:#607d8b}.help-box>*{font-weight:bold}.help-text{text-align:justify;line-height:1.5em;margin-top:1em}.tooltip{width:300px;max-width:300px;background-color:#777;color:#fff;z-index:101}.cursor-pointer{cursor:pointer}.password-actions{display:inline-block;width:12em}.password-actions>span,.password-actions i{margin-right:.6em}.custom-input-color{width:3em;height:1em;display:inline-block}.account-pass-image{height:32px;width:auto}.select-box{min-width:20em}fieldset.warning{padding:8px;border-radius:5px;color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}fieldset.warning legend{color:#ef5350 !important}fieldset.warning a{color:#ef5350 !important;font-weight:bold}#actions{width:100%;line-height:2em;margin-bottom:5em}#actions #logo{display:flex;width:100%;margin-bottom:30px;color:#607d8b;align-items:center;background:url("../imgs/logo_full_bg.png") left no-repeat;background-size:auto 150px;height:150px}#actions #page-title{width:100%;color:#607d8b;text-align:center}#actions #page-title h1{font-weight:bold;font-size:24px;letter-spacing:3px}#actions ul.errors{max-width:40vw;margin:0 auto;list-style:none;font-size:14px;text-align:left}#actions ul.errors>li{margin:1.5em auto;border-radius:3px;padding:1em .5em}#actions ul.errors>li.msg-critical{color:#ef5350;background-color:#ffebee;border:1px solid #ef5350}#actions ul.errors>li.msg-warning{color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28;color:#555}#actions ul.errors>li.msg-ok{color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#actions ul.errors>li>p.hint{color:#555;font-size:12px}#actions ul.errors>li>p.hint i{margin-right:.5em}#actions form{width:450px;margin:0 auto;text-align:left}#actions form fieldset{margin-bottom:2em}#actions form fieldset legend{width:100%;color:#fff;font-size:14px;font-weight:bold;text-align:center;background-color:#607d8b;margin:1em 0;letter-spacing:.2em;padding:.2em 0}#actions div.buttons{margin-top:2em;text-align:center}.center{text-align:center !important}.right{text-align:right !important}.left{text-align:left !important}.opacity50{opacity:.5 !important}#login-container{width:40em;margin:0 auto;background:transparent url("../imgs/logo_full_bg.png") no-repeat top left;background-size:auto 10em}#login-container #boxSpacer{height:11em;background-color:transparent}#login-container #boxLogin{position:relative;margin:0 auto;width:100%;min-height:14em;padding:1em;background-color:#fff}#login-container #boxLogin #boxData{height:100%;min-height:14em;text-align:left;background-color:transparent}#login-container #boxLogin #boxData i{margin-right:.5em;opacity:.5}#login-container #boxLogin #boxData .extra-hidden{display:none}#login-container #boxLogin #boxButton{position:absolute;top:2em;right:2em}#login-container #boxLogin #boxActions{width:100%;text-align:right}#login-container #boxLogin #boxActions a{color:#c9c9c9}#login-container #boxLogout{margin-top:4em;width:100%}#login-container #boxLogout>div{margin:0 auto;width:250px;font-size:14px;padding:.5em;text-align:center;color:#ffca28;background-color:#fff8e1;border:1px solid #ffca28}#login-container #boxUpdated{width:350px;margin:3em auto;font-size:14px;text-align:center;padding:.5em;color:#26a69a;background-color:#e0f2f1;border:1px solid #26a69a}#login-container #demo-info{margin:3em auto;color:#c9c9c9;border-top:1px solid #d9d9d9;border-bottom:1px solid #d9d9d9;padding:.5em}#login-container #demo-info ul{display:flex;justify-content:space-around;list-style:none}#login-container #demo-info ul li span{margin:0 2em}@media screen and (max-width:1000px){#content #searchbox .search-filters>*{margin:.5em 1em .5em 0}#content .data-container #title,#content .tab-data #title{width:90%}footer{display:none;justify-content:space-between;flex-wrap:wrap}footer .footer-parts{justify-content:space-between;flex-wrap:wrap}footer #footer-left,footer #footer-right{width:100%}footer .footer-parts>div{width:100%;padding:.5em 0}}@media screen and (max-width:600px){#content input,#box-popup input{width:100%}#content .mdl-textfield,#box-popup .mdl-textfield{width:100%}#content td.descField,#box-popup td.descField{display:none}#content td.valField .lowres-title,#box-popup td.valField .lowres-title{display:block}#content #searchbox .mdl-textfield{width:90%}#content #searchbox .search-text{width:90%}#content #searchbox .search-filters .selectize-control{width:100%}#content .data-container,#content .tab-data{width:100%}#content .data-container #title,#content .tab-data #title{width:90%}#content .data-container .selectize-control,#content .tab-data .selectize-control{width:100%}#box-popup h2>.btn-popup-close{display:inline-block}.mdl-data-table{table-layout:fixed;width:100%}.table-responsive td,.table-responsive th{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis}.table-responsive .cell-actions i{display:block !important}}
\ No newline at end of file
diff --git a/inc/themes/material-blue/css/styles.scss b/inc/themes/material-blue/css/styles.scss
index 75f8d333..77727632 100644
--- a/inc/themes/material-blue/css/styles.scss
+++ b/inc/themes/material-blue/css/styles.scss
@@ -532,8 +532,8 @@
}
#box-popup {
- min-width: 25em;
- max-width: 50em;
+ min-width: 30em;
+ max-width: 60em;
margin: 5em auto;
padding: 0;
background-color: #fff;
diff --git a/inc/themes/material-blue/js/app-theme.js b/inc/themes/material-blue/js/app-theme.js
index f0c924d9..942952b7 100644
--- a/inc/themes/material-blue/js/app-theme.js
+++ b/inc/themes/material-blue/js/app-theme.js
@@ -114,9 +114,12 @@ sysPass.Theme = function (Common) {
// Poner la clave en los input y actualizar MDL
$dstParent.find("input:password").val(genPassword);
$dstParent.addClass(mdl.CssClasses_.IS_DIRTY).removeClass(mdl.CssClasses_.IS_INVALID);
+
// Poner la clave en el input de repetición y encriptarla
- $targetR.val(genPassword).parent().addClass(mdl.CssClasses_.IS_DIRTY).removeClass(mdl.CssClasses_.IS_INVALID);
- Common.encryptFormValue($targetR);
+ if ($targetR.length > 0) {
+ $targetR.val(genPassword).parent().addClass(mdl.CssClasses_.IS_DIRTY).removeClass(mdl.CssClasses_.IS_INVALID);
+ Common.encryptFormValue($targetR);
+ }
// Mostar el indicador de complejidad
$dstParent.find("#passLevel").show(500);
@@ -127,8 +130,6 @@ sysPass.Theme = function (Common) {
}
};
-
- // FIXME
// Diálogo de configuración de complejidad de clave
var complexityDialog = function () {
@@ -193,8 +194,6 @@ sysPass.Theme = function (Common) {
var $thisParent = $this.parent();
var targetId = $this.attr("id");
- var $targetIdR = $("#" + targetId + "R");
-
var btnMenu = "";
@@ -234,7 +233,12 @@ sysPass.Theme = function (Common) {
// Reset de los campos de clave
$passwordActions.find(".reset").on("click", function () {
$this.val("");
- $targetIdR.val("");
+
+ var $targetIdR = $("#" + targetId + "R");
+
+ if ($targetIdR.length > 0) {
+ $targetIdR.val("");
+ }
// Actualizar objetos de MDL
componentHandler.upgradeDom();
diff --git a/inc/themes/material-blue/js/app-theme.min.js b/inc/themes/material-blue/js/app-theme.min.js
index df2dbcb8..55798540 100644
--- a/inc/themes/material-blue/js/app-theme.min.js
+++ b/inc/themes/material-blue/js/app-theme.min.js
@@ -1,18 +1,18 @@
-var $jscomp={scope:{},findInternal:function(a,g,e){a instanceof String&&(a=String(a));for(var h=a.length,k=0;k=c||58<=c&&64>=c||91<=c&&96>=c||123<=c&&126>=c)||!a.passwordData.complexity.numbers&&48<=c&&57>=c||!a.passwordData.complexity.uppercase&&65<=c&&90>=c||(d++,f+=String.fromCharCode(c));$("#viewPass").attr("title",f);var e=zxcvbn(f);a.passwordData.passLength=f.length;b?(d=b.parent(),c=$("#"+b.attr("id")+"R"),a.outputResult(e,b),b=new MaterialTextfield,d.find("input:password").val(f),d.addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID),
-c.val(f).parent().addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID),a.encryptFormValue(c),d.find("#passLevel").show(500)):(a.outputResult(e),$("input:password, input.password").val(f),$("#passLevel").show(500))},k=function(){var b='";showDialog({title:a.config().LANG[29],text:b,negative:{title:a.config().LANG[44]},positive:{title:a.config().LANG[43],onClick:function(d){d.preventDefault();a.passwordData.complexity.numbers=$("#checkbox-numbers").is(":checked");
a.passwordData.complexity.uppercase=$("#checkbox-uppercase").is(":checked");a.passwordData.complexity.symbols=$("#checkbox-symbols").is(":checked");a.passwordData.complexity.numlength=parseInt($("#passlength").val())}},cancelable:!0,contentStyle:{"max-width":"300px"},onLoaded:function(){$("#checkbox-numbers").prop("checked",a.passwordData.complexity.numbers);$("#checkbox-uppercase").prop("checked",a.passwordData.complexity.uppercase);$("#checkbox-symbols").prop("checked",a.passwordData.complexity.symbols);
-$("#passlength").val(a.passwordData.complexity.numlength)}})},m=function(b){b.find(".passwordfield__input").each(function(){var d=$(this);if("true"!==d.attr("data-pass-upgraded")){var f=d.parent(),c=d.attr("id"),b=$("#"+c+"R"),l='',l=l+(''),
e=$(''),c=$('');a.forEach(function(a){var d=c.clone();d.append('person');d.append(a);a=e.clone().append(d);b.append(a)});return b},tabs:{add:function(a,d,e,c){a=$(a);var b="";1===c&&(a.parent().find("#tabs-"+d).addClass("is-active"),b="is-active");a.append(''+e+"")}}}}};
diff --git a/inc/themes/material-blue/views/itemshow/tokens.inc b/inc/themes/material-blue/views/itemshow/tokens.inc
index 8e17ac83..f15112aa 100644
--- a/inc/themes/material-blue/views/itemshow/tokens.inc
+++ b/inc/themes/material-blue/views/itemshow/tokens.inc
@@ -1,3 +1,7 @@
+