Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8eb4caf456 | ||
|
|
bd550841bd | ||
|
|
5709c5c856 | ||
|
|
dabdfa1354 |
@@ -2180,13 +2180,58 @@ class AcaiAPI
|
||||
$childNums[(int) $row['parentNum']][] = $row['num'];
|
||||
}
|
||||
|
||||
$before = $byNum;
|
||||
|
||||
self::_rebuildCategoryBranch([
|
||||
'branchParent' => 0,
|
||||
'records' => &$byNum,
|
||||
'childNodes' => $childNums,
|
||||
]);
|
||||
|
||||
// Solo las filas que cambian, y todas en UNA transaccion. Antes era un
|
||||
// UPDATE por fila de la tabla entera en autocommit: en InnoDB cada uno
|
||||
// es un commit a disco (8.168 filas = 1,9 s; en una transaccion, 0,7 s),
|
||||
// y mover un nodo deja casi siempre intacta buena parte del arbol.
|
||||
// START TRANSACTION por SQL y no con PDO: `Db` no expone la conexion, y
|
||||
// anadirle un metodo aqui romperia las webs con un Db.php propio (el
|
||||
// sync de web-base respeta los ficheros personalizados). En MyISAM es
|
||||
// inocuo.
|
||||
$changed = [];
|
||||
foreach ($byNum as $num => $cat) {
|
||||
if (self::_categoryRowChanged($before[$num], $cat)) $changed[$num] = $cat;
|
||||
}
|
||||
if (!$changed) return;
|
||||
|
||||
$db->execute("START TRANSACTION");
|
||||
try {
|
||||
self::_writeCategoryRows($db, $fullTable, $changed);
|
||||
$db->execute("COMMIT");
|
||||
} catch (\Throwable $e) {
|
||||
$db->execute("ROLLBACK");
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/** ¿Cambia alguno de los campos que recalcula el arbol? PDO devuelve
|
||||
* tipos nativos y el recalculo mezcla int y string: se compara como
|
||||
* texto, salvo NULL, que solo es igual a NULL. */
|
||||
private static function _categoryRowChanged(array $before, array $after)
|
||||
{
|
||||
foreach (['globalOrder', 'siblingOrder', 'depth', 'lineage', 'breadcrumb'] as $field) {
|
||||
$a = isset($before[$field]) ? $before[$field] : null;
|
||||
$b = isset($after[$field]) ? $after[$field] : null;
|
||||
if ($a === null || $b === null) {
|
||||
if ($a !== $b) return true;
|
||||
} elseif ((string) $a !== (string) $b) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function _writeCategoryRows($db, $fullTable, array $rows)
|
||||
{
|
||||
foreach ($rows as $num => $cat) {
|
||||
$db->execute(
|
||||
"UPDATE `$fullTable`
|
||||
SET globalOrder = ?,
|
||||
|
||||
@@ -406,10 +406,13 @@ function _renameFile() {
|
||||
* (install/uninstall/enable/disable) quedaban inservibles ahi con un
|
||||
* "Token no valido" que no tenia nada que ver con el token.
|
||||
*
|
||||
* No es un bypass: se compara el par token+tokenHash recibido con el que la
|
||||
* propia web guarda en su .acai, que es exactamente el que Forge envia. Sin
|
||||
* .acai (produccion), con mode=production, o sin coincidencia exacta, no
|
||||
* autoriza nada.
|
||||
* No es un bypass: se compara un secreto recibido en el cuerpo con el que la
|
||||
* propia web guarda en su .acai. Sin .acai (produccion), con mode=production,
|
||||
* o sin coincidencia exacta, no autoriza nada. Dos formas de coincidir:
|
||||
* - secretoLocal (cuerpo) == secreto_local (.acai): el secreto de la copia,
|
||||
* que no es una credencial de ninguna persona. Es la via buena.
|
||||
* - token+tokenHash (cuerpo) == token+tokenHash (.acai): la de siempre.
|
||||
* Las cadenas vacias nunca coinciden. Los secretos no se registran nunca.
|
||||
*/
|
||||
function _lifecycleTokenLocalValido($datosAuth) {
|
||||
$acaiPath = __DIR__ . "/../../../.acai";
|
||||
@@ -420,10 +423,24 @@ function _lifecycleTokenLocalValido($datosAuth) {
|
||||
if (!is_array($acai) || @$acai["mode"] === "production") {
|
||||
return false;
|
||||
}
|
||||
$token = (string) @$datosAuth["token"];
|
||||
$hash = (string) @$datosAuth["tokenHash"];
|
||||
$tokenLocal = (string) @$acai["token"];
|
||||
$hashLocal = (string) @$acai["tokenHash"];
|
||||
if (!is_array($datosAuth)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Via buena: el secreto propio de la copia local.
|
||||
$secretoLocal = @$acai["secreto_local"];
|
||||
$secreto = @$datosAuth["secretoLocal"];
|
||||
if (is_string($secretoLocal) && $secretoLocal !== ""
|
||||
&& is_scalar($secreto) && (string) $secreto !== ""
|
||||
&& hash_equals($secretoLocal, (string) $secreto)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// transitorio: quitar cuando el .acai ya no lleve token (fase 1d)
|
||||
$token = is_scalar(@$datosAuth["token"]) ? (string) $datosAuth["token"] : "";
|
||||
$hash = is_scalar(@$datosAuth["tokenHash"]) ? (string) $datosAuth["tokenHash"] : "";
|
||||
$tokenLocal = is_scalar(@$acai["token"]) ? (string) $acai["token"] : "";
|
||||
$hashLocal = is_scalar(@$acai["tokenHash"]) ? (string) $acai["tokenHash"] : "";
|
||||
if ($token === "" || $hash === "" || $tokenLocal === "" || $hashLocal === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user